Попробуем немного разобраться.
Откуда взялось так много парадигм?
На самом деле ответ уже прозвучал в этом посте — разные типы задач легче и быстрее решать, используя подходящие парадигмы. Соответственно, с развитием ИТ появлялись новые виды задач (или старые становились актуальными), а решать их используя старые подходы было неудобно, что влекло за собой переосмысление и появление новых методик.
Что выбрать?
Все зависит от того, что требуется сделать. Стоит отметить, что все средства разработки отличаются, одни поддерживают одно, другие — другое. К примеру, PHP со «стандартным» набором модулей не поддерживает аспектно-ориентированное программирование. Поэтому выбор методологии довольно тесно связан с платформой разработки. Ну, и не стоит забывать, что можно комбинировать разные подходы, что приводит нас к выбору стека парадигм.
Для категоризации парадигм я использую 4 измерения, которые присущи практически любой задаче:
- Данные
Любая программа, так или иначе, работает с данными: хранит их, обрабатывает, анализирует, передает - Действия.
Любая программа должна что-то делать, обычно действия связанны с данными. - Логика.
Логика или бизнес-логика определяет правила, которым подчиняются данные и действия. Без логики программа лишена смысла - Интерфейс.
То, как программа взаимодействует с внешним миром
Можно пойти дальше и углубиться в эту идею: придумать качественные характеристики для данных четырех мер, добавить строгие правила и немного математики, но это, пожалуй, тема для отдельного поста. Я думаю, большинство системных архитекторов определяют данные характеристики для конкретной задачи на основе своих знаний и опыта.
После того, как вы проанализируете свою задачу по этим 4 измерениям, то, скорее всего, увидите, что определенное измерение выражено сильнее остальных. А это в свою очередь позволит определиться с парадигмой программирования, так как обычно они нацелены на какое-то одно измерение.
Рассмотрим примеры:
- Ориентация на данные (Data Driven Design).Нам важны в первую очередь данные, а не то, как они между собой связанны.
Типы подходящих приложений:
- грабберы (собираем данные из разных источников, сохраняем куда-нибудь)
- различные админки, интерфейсы к базам данным, все, где много простых CRUD операций
- случаи, когда работа с данными уже определена, например, требуется разработать программу, а база данных уже существует и схему данных не изменить. В таком случае возможно проще ориентироваться на то, что уже есть, чем создавать дополнительные обертки над данными и уровнями доступа к данным
- часто ориентация на данные появляется при использовании ORM, но нельзя заранее сказать хорошо это, или плохо (об этом ниже)
- Ориентация на действия — императивные подходы к разработке. Думаю, что сюда можно отнести такие парадигмы как Event Driven Programming, Aspect Oriented Programming.
- Ориентация на логику — Domain Driven Design (DDD) и все, что с этим связанно. Здесь нам важна предметная область задачи, мы уделяем внимание моделированию объектов, анализу связей и зависимостей. Применяется преимущественно в бизнес приложениях.
Так же, сюда относится декларативный подход и отчасти функциональное программирование (решение задач, которые хорошо описываются математическими формулами) - Ориентация на интерфейс. Используется, когда в первую очередь важно как программа взаимодействует с внешним миром.
Разработка приложения с ориентацией только на интерфейс — ситуация довольно редкая. Хотя в некоторых книгах я встречал упоминание о том, что такой подход рассматривался в серьез, причем базируясь на пользовательском интерфейсе — брали, что непосредственно видит пользователь и, исходя из этого, проектировали структуры данных и все остальное.
Ориентация на пользовательский интерфейс в бизнес приложениях часто проявляется косвенно: к примеру, пользователю требуется видеть определенные данные, которые получить сложно, за счет чего архитектура обрастает дополнительными конструкциями (например, вынужденной избыточностью данных).
Формально сюда можно отнести Event Driven Programming
А, что на практике?
В нашей компании мы занимаемся разработкой бизнес приложений (стартапы, веб-сервисы, сайты, прикладные программы и т.п.) поэтому далее поговорим о парадигмах программирования, которые встречаются как в нашей практике, так и у других команд работающих в этой области.
На основе моего опыта могу сказать, что в данной сфере превалируют два подхода: ориентация на данные (Data Driven) и ориентация на логику (Domain Driven). По сути, они являются конкурирующими методологиями, но на практике могут объединяться в симбиозы, которые часто являются известными анти-паттернами.
Одним из преимуществ Data Driven в сравнении с Domain Driven является простота использования и внедрения. Поэтому Data Driven начинают использовать там, где надо применить Domain Driven (причем часто это происходит бессознательно). Проблемы же возникают с тем, что Data Driven плохо совместим с концепциями объектно-ориентированного программирования (конечно, если вы вообще используете ООП). На небольших приложениях эти проблемы почти незаметны. На средних по размеру приложениях эти проблемы уже заметны и начинают приводить к анти-паттернам, ну, а на крупных проектах проблемы становятся серьезными и требуют соответствующих мер.
В свою очередь Domain Driven выигрышен на крупных проектах, а на небольших — приводит к усложнению решения и требует больше ресурсов для разработки, что часто бывает критичным с точки зрения бизнес требований (вывести проект на рынок «asap», за небольшой бюджет).
Для того, чтобы понять разницу в подходах рассмотрим более конкретный пример. Допустим, нам требуется разработать систему учета ордеров. У нас есть такие сущности как:
- Продукт
- Клиент
- Квота (отправляется клиенту как предложение)
- Ордер (заказ)
- Инвойс (платежка)
- Ордер поставщику
- Билл (по сути, платежка от поставщика)
Решив, что контекстная область у нас как на ладони, мы начинаем проектировать базу данных. Создаем соответствующие таблицы, запускаем ORM-ку, генерируем сущностные классы (ну, или в случае «умной» orm-ки прописываем схему где-то отдельно, например, в хml, и уже по ней генерируем и базу и сущностные классы). В итоге получаем на каждую сущность отдельный, независимый класс. Радуемся жизни, работать с объектами легко и просто.
Проходит время, и нам требуется добавить дополнительную логику в программу — например, находить у ордера товар с самой высокой ценой. Здесь уже могут возникнуть проблемы, если ваша orm не поддерживает внешние связи (т.е. сущностные классы ничего не знаю о контексте данных), в этом случае, придется создавать сервис, в котором будет метод — по ордеру вернуть нужный продукт. Но, наша orm хорошая, умеет работать с внешними связями, и мы просто добавляем метод в класс ордера. Снова радуемся жизни, цель достигнута, в класс добавлен метод, у нас почти настоящее ООП.
Проходит время, и нам надо добавить такой же метод и для квоты, и для инвойса и для других аналогичных сущностей. Что делать? Мы можем просто прописать во все классы этот метод, но это будет, по сути, дублирование кода и аукнется при поддержке и тестировании. Мы не хотим усложнять и просто копируем метод во все классы. Потом появляются аналогичные методы, сущностные классы начинают распухать одинаковым кодом.
Проходит время, и появляется логика, которую не описать внешними связами в бд, и поэтому разместить ее в сущностных классах нет возможности. Мы начинаем создавать сервисы, которые выполняют эти функции. В итоге получаем, что бизнес логика разбросана по сущностным классам и сервисам, понять, где искать нужный метод становится все сложнее. Решаем провести рефакторинг и вынести, к примеру, повторяющейся код в сервисы — выделяем общий функционал в интерфейс (например, делаем интерфейс — IProductable, т.е. нечто, что содержит продукты), сервисы могут работать с этими интерфейсами, за счет чего немного выигрываем в абстракции. Но кардинально это не решает проблему, получаем больше методов в сервисах и решаем для единства картины перенести все методы из сущностных классов в сервисы. Теперь мы знаем, где искать методы, но наши сущностные классы лишись всякой логики, и мы получили так называемую «Анемичную модель» (Anemic Model).
На этом этапе мы полностью ушли от концепции ООП — объекты хранят только данные, вся логика находится в отдельных классах, ни инкапсуляции, ни наследования.
Стоит отметить, что это не так плохо, как может показаться — ничего не мешает внедрять юнит тестирование, да и вообще разработку через тестирование, внедрять паттерны управления зависимостями и т.п., в общем, с этим можно жить. Проблемы возникнут, когда приложение перерастет в большое, когда сущностей станет так много, что их нереально удержать в голове, а о взаимодействии и говорить не приходится. В этом случае поддержка и развитие такого приложения станет проблемой.
Как вы наверно догадались, этот сценарий описывает использование Data Driven подхода и его проблемы.
В случае с Domain Driven мы бы поступили следующим образом. Во-первых, ни о каком проектировании бд на первом этапе речи бы не шло. Нам потребовалось бы тщательно проанализировали контекстную область задачи, смоделировать ее и перенести на язык ООП.
Например, мы можем создать модель абстрактного документа, у которого был бы набор базовых свойств. От него наследовать документ, у которого есть продукты, от него наследовать «платежный» документ, с ценой и биллинг адресом и так далее. При таком подходе добавить метод, получающий самый дорогой продукт, не составляет труда — мы просто добавим его в соответствующий базовый класс.
В итоге, контекстная область задачи будет описана используя ООП на полную катушку.
Но появляются очевидные проблемы: как сохранять данные в бд? Собственно, для этого потребуется создавать функционал для меппирования данных из наших моделей на поля в бд. Такие мепперы могут быть довольно сложными, и при изменении моделей придется изменять и мепперы.
Более того, вы не застрахованы от ошибки при моделировании, что может привести к сложному рефакторингу.
Итак, подведем итог Data Driven vs Domain Driven:
Data Driven:
- Плюсы
- Позволяет быстро разработать приложение или прототип
- Удобно проектировать (кодогенерация по схеме и тп)
- На небольших или средних по размеру проектах может быть вполне себе решением
- Минусы
- Может приводить к анти-паттернам и уходу от ООП
- На больших проектах приводит к хаосу, сложной поддержке и т.п.
Domain Driven:
- Плюсы
- Использует всю мощь ООП
- Позволяет контролировать сложность контекстной области (домена)
- Есть еще ряд преимуществ, не описанных в статье, например, создание доменного языка и внедрение BDD.
- Дает мощный инструмент для разработки сложных и больших решений
- Минусы
- Требует значительно больше ресурсов при разработке, что приводит к удорожанию решения
- Определенные части становятся сложнее в поддержке (мепперы данных и т.п.)
Так, что же, черт возьми, мне выбрать?
К сожалению, однозначного ответа нет. Анализируйте вашу проблему, ваши ресурсы, перспективы развития, цели и задачи. Правильный выбор — это всегда компромисс.
ссылка на оригинал статьи http://habrahabr.ru/post/158277/
Добавить комментарий