Страницы

вторник, 28 января 2014 г.

О фреймворках и свободе

“Множество переусложненных решений (overengineering) было принято во имя гибкости. Но дополнительные уровни абстракции и уровни косвенности гораздо чаще мешают, нежели помогают в этом деле. Посмотрите на дизайн систем, которые действительно вдохновляют программистов, занимающихся их разработкой, и как правило вы увидите нечто простое. Простого решения добиться не просто (simple is not easy).”

Эрик эванс "Domain-Driven Design: Tackling Complexity in the Heart of Software"

Обсуждая те или иные вопросы дизайна уже неоднократно поднимался вопрос гибкости vs. простоты решения. С одной стороны, гибкость очень привлекательна. Что может быть лучше, чем возможность на лету адаптировать приложение под изменившиеся требования? И хотя эта мысль очень заманчива, большинство опытных разработчиков знают, что она утопична, если она приходит в начале работы над проектом.

Проблема в том, что, как и любая оптимизация, обобщение должно приниматься осознанно после «профилирования» существующих решений и четкого осознания возможности и необходимости универсальности, в чем бы она не заключалась.

Любое решение, которое само подстраивается под изменение требований (переход от одной UI-технологии к другой; поддержка разных баз данных; возможность изменять правила бизнес-логики на лету) существенно усложняет систему, при этом на ранних этапах мы просто не знаем, в какую именно сторону повеет «ветер перемен» в голове заказчика и что именно будет меняться.

О фреймворках

Апогеем гибкости и повторного использования является создание фреймворков приложений. Нет ничего более ужасного, чем раскапывание бизнес-логики приложения в дебрях самописного фреймворка, который как швейцарский нож умеет сохранять, читать, отображать, конфигурировать и обязательно сериализировать все в XML.

Нет, я не против инфраструктурных фреймворков, таких как WCF, Entity Framework или WPF. Они предназначены для решения четко поставленной задачи, проектируются группой неглупых людей, удобны для решения простых задач и позволяют решать более сложные задачи путем [примерно] линейного увеличения трудозатрат. Но даже у них, решение любой нетривиальной задачи превращается в борьбу не с самой задачей, а с самим фреймворком, что подтвердит каждый, кто копнул эти фреймворки глубже некоторого уровня.

В отличии от инфраструктурных фреймворков, большинство фреймворков приложений начинают создаваться группой энтузиастов, которые «знают лучше, что нужно разработчику приложений» и продавливают (зачастую через верхний менеджмент) свое творение. Иногда эти фреймворки разрабатываются и используются одной и той же командой, при этом большинство членов команды просто не замечает, как из затягивает трясина сложности.

Я помню, как минимум два характерных примера использования фреймворков, самым характерным из которых был следующий. Нашей команде пытались продать фрейморк, который умел практически все, но в контексте WPF-приложений. Во-первых, базовая вью-модель принимала 3 аргумента конструктора (ILogger, IView и чего-то еще). Во-вторых, в нем был встроенный пул потоков (!), построенный на основе массива из 20 (!) потоков, которые создавались по старту. В-третьих, он был пропитан контейнерами и код инициализации модуля состоял из нескольких страниц. В результате мы успешно от него отбрыкались, а некоторые его пользователи начинали теряться в своих собственных решениях уже во время работы над своим проектом.

На самом деле, во фреймворках приложений (application framework) нет ничего особенно плохого, если они используются по делу. Тот же Эрик Эванс в своей замечательной книге “Domain-Driven Design” рассматривает PLUGGABLE COMPONENT FRAMEWORK, как один из высокоуровневых шаблонов разработки приложений, но, как и любой здравомыслящий человек, он предлагает его использовать лишь после разработки нескольких однотипных приложений, когда понятно, как и что нужно обобщать, что является общим знаменателем и как именно будут эти компоненты общаться между собой.

Но даже и в этом случае, разработка такого фреймворка требует большого опыта от своих разработчиков и обильной обратной связи от своих пользователей. Но даже в этом случае остается риск того, что требования некоторых пользователей будут несогласованы с возможностями фрейморка, что сделает его использование в некоторых местах похожим на таскание чемодана без ручки.

О свободе

Интересно, что я несколько раз уже слышал, что гибкие решения вообще и фреймворки в частности обеспечивают свободу разработчику. По своему определению, фреймворк представляет собой каркас, куда разработчик добавляет недостающие элементы для получения полноценного решения (помните о разнице между фреймворком и библиотекой?). Фреймворк или библиотека может снимать груз с плеч разработчика, чтобы он смог сосредоточиться на главном – на бизнес-логике приложения, но он не может по своему определению давать больше свободы, чем его полное отсутствие.

Когда я думаю о бизнес логике, то обычно не хочу думать ни о чем другом. Я не хочу думать о коммуникациях, о персистентности, о пользовательском интерфейсе и уж точно я не хочу думать о кастомных фреймворках. Это не всегда возможно, но подобные стремления являются моим основным инструментом борьбы со сложностью.

Не зря большинство авторов (Буч, Эванс, Мейер) сходятся в мысли, что «чем сложнее класс или компонент, тем меньше у него должно быть внешних связей». У того же Эванса это выражается за счет помещения ключевой бизнес-логики в неизменяемые объекты, полностью изолированные от внешнего мира. Другие авторы предлагают другие подходы, но суть их остается неизменной.

Я помню один довольно забавный случай во время ревью кода, когда довольно простая задача импорта данных из одного источника с последующим экспортом в другой была полностью сокрыта за сложностью кастомного фреймворка. Код должен говорить о своем намерении четко и ясно, а не прятать свою суть за кучей ненужных слоев.

Тут самое главное помнить о том, что все наши инструменты предназначены для упрощения решения тех или иных задач, а если они не упрощают, а усложняют, то нужно от них либо отказываться, либо учиться использовать их по назначению.

Ссылки по теме

2 комментария: