д
орога
,
вымощенная
Благими
намерениями
Предыдущие разделы, возможно, звучат как обвинение разработчиков при-
ложений во всех проблемах, связанных с производительностью, и в их не-
желании «думать как база данных». Но обвинять кого-либо – неэффектив-
ный способ решения любых проблем, включая и низкую производительность
приложений. Более продуктивно было бы попытаться понять, как благие
намерения могут привести к таким тяжелым результатам.
Начнем с изучения шаблонов, которым рекомендуют следовать разработ-
чикам приложений.
Шаблоны разработки приложений
Самый распространенный шаблон проектирования в современной про-
граммной инженерии – это многоуровневая архитектура. Обычно выделяют
четыре уровня:
интерфейс конечного пользователя;
бизнес-логика;
персистентность;
база данных.
Каждый уровень может взаимодействовать только со смежными уровня-
ми, а инкапсуляция и независимость поощряется и внутри каждого уровня,
и, конечно, между уровнями. Таким образом, бизнес-объект
customer
(клиент)
ничего не знает про таблицу базы данных
Customer
и фактически может быть
подключен к любой произвольной базе данных, если на уровне персистент-
ности определены соответствия между данными в базе и объектами на уров-
не бизнес-логики.
Для этого есть несколько важных причин, главные из которых – ускорение
разработки, удобство сопровождения и простота модификации приложе-
ния, а также возможность многократного использования компонентов. На
первый взгляд кажется очевидным, что изменение интерфейса конечного
пользователя не должно де-факто вызывать изменение схемы базы дан-
ных. Строгое разделение также способствует быстрой совместной работе:
разработчики могут заниматься разными частями приложения и быть уве-
ренными, что изменения, которые они вносят во внутреннюю структуру
или реализацию объектов, никак не повлияют на другие части приложения.
Дорога, вымощенная благими намерениями
185
И конечно, кажется полезным, что на основе одной и той же бизнес-логики
могут создаваться несколько приложений – внутренняя логика приложения
не должна дублироваться для каждого нового окружения.
Пока все хорошо, так в чем проблема? К сожалению, есть много подвод-
ных камней, и методология не совсем обеспечивает обещанную выгоду – по
крайней мере в том виде, в каком она применяется в реальных условиях.
Рассмотрим идею централизации бизнес-логики. Во-первых, преимущест-
ва объединения всей логики в одном месте, на бизнес-уровне, несколько
уменьшаются, когда это «одно место» составляют несколько сотен тысяч
строк кода. На практике такой большой бизнес-уровень приводит к дублиро-
ванию кода или, что еще хуже, к попыткам дублирования кода. Когда уровень
бизнес-логики увеличивается в размерах, трудно найти функцию, которая
делает именно то, что нужно. В результате авторы часто наблюдали одну и ту
же бизнес-логику, реализованную разными способами, разными методами
и с разными результатами.
Во-вторых, бизнес-логика может быть доступна для дополнительных
пользовательских интерфейсов, но она недоступна для других бизнес-при-
ложений, которые напрямую взаимодействуют с базой данных, – в первую
очередь для создания отчетов. Таким образом, авторы отчетов в конечном
итоге дублируют логику приложения или в хранилище данных, или, что еще
хуже, в отдельных отчетах, без гарантий эквивалентности исходной логике
приложения.
Кроме того, при таком подходе взаимодействие с уровнем персистент-
ности ограничивается отдельными объектами или даже отдельными ска-
лярными значениями, что сводит на нет возможности движка базы данных.
Интерфейс конечного пользователя может знать все необходимые ему эле-
менты данных, но, поскольку он не взаимодействует напрямую с уровнем
персистентности, запросы данных опосредуются уровнем бизнес-логики.
Типичная реализация уровня персистентности содержит классы досту-
па к данным, которые однозначно соответствуют классам бизнес-объектов.
Базовые DML-функции (
INSERT
,
UPDATE
,
DELETE
) написать легко, но что проис-
ходит, когда операции должны выполняться над множеством объектов этого
класса? Есть два пути: разработчик может создать еще один набор методов,
которые будут повторять те же функции для объектов из множества. Однако
это нарушит принцип повторного использования кода. Есть альтернативный
и более распространенный вариант: разработчик просто перебирает элемен-
ты коллекции, вызывая функции, определенные для обработки отдельных
объектов.
Представьте себе интерфейс приложения, в котором перечислены все пас-
сажиры, вылетевшие из аэропорта О’Хара. Разработчик базы данных сооб-
разит, что для составления списка всех пассажиров, вылетевших из аэро-
порта, необходимо соединить таблицу
flight
с таблицей
boarding_pass
. Вся
информация будет получена за одно обращение. Для разработчика прило-
жения задача может оказаться сложнее. У него может быть метод
GetFlight-
ByDepartureAirport
, который принимает код аэропорта в качестве параметра
и возвращает набор рейсов. Затем он может перебрать рейсы, возвращая все
посадочные талоны на рейс. Фактически он реализует алгоритм соединения
186
Разработка приложений и производительность
вложенными циклами внутри приложения.
Чтобы избежать этого, можно использовать несколько разных решений.
Можно добавить атрибут аэропорта вылета к объекту посадочного талона. Но
тогда возможны проблемы с целостностью данных: что, если время вылета
рейса обновится в записи рейса, но не во всех посадочных талонах? Можно
определить метод получения посадочных талонов для данного аэропорта
вылета, но это нарушит правило, согласно которому объекты не знают друг
о друге. При чистом многоуровневом подходе объект посадочного талона
ничего не знает об объекте рейса, а объект рейса ничего не знает про объект
посадочного талона. Метод, извлекающий данные для обоих объектов, не
может принадлежать ни к одному из них.
Do'stlaringiz bilan baham: |