Автор: Sergey Teplyakov

Наверняка многие слышали о принципе инверсии зависимостей, букве D из аббревиатуры SOLID. Изначально этот принцип был описан Бобом Мартином еще в 1996 году в статье для C++ Report. Затем эта же статья в расширенном виде вошла в книги дядюшки Боба Agile Software Development, Principles, Patterns and Practices и затем в Agile Principles, Patterns and Practices in C#.

В исходной статье Боба Мартина есть 3 части: философские размышления о хорошем и плохом дизайне, описание принципа инверсии зависимости и простой пример с лампочками и кнопками. Вторая часть статьи весьма известна, последняя малоинтересна, а вот первую часть, ИМХО, незаслуженно обходят вниманием.

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

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

Итак, перед вами перевод раздела статьи Dependency Inversion Principle под название Что пошло не так с ПО? (What goes wrong with software?).

Что пошло не так с ПО?

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

Большинство разработчиков не стремятся к плохому дизайну, при этом для многих систем наступает момент, когда начинают говорить, что ее дизайн прогнил. Почему так происходит? Был ли дизайн плохим с самого начала или дизайн загнил с течением времени? Корнем этой проблемы является отсутствие хорошего определения плохого дизайна.

Определение плохого дизайна

Проводили ли вы инспекцию дизайна со своим коллегой, которым вы особенно гордились? При этом ваш коллега говорил вам насмешливо что-то типа: Мда А почему ты реализовал это именно так? Это очень часто происходило со мной, и я видел, как это происходило со многими другими разработчиками. Конечно же, двое несогласных коллег никогда не используют одинаковые критерии для определения плохого дизайна. Самым распространенным критерием, с которым я сталкивался, был синдром АВЯБСЭНТ или А вот я бы сделал это не так (TNTWIWHDI, Thats not the way I would have done it).

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

  1. Его тяжело изменить, поскольку любое изменение влияет на слишком большое количество других частей системы. (Жесткость, Rigidity).
  2. При внесении изменений неожиданно ломаются другие части системы. (Хрупкость, Fragility).
  3. Код тяжело использовать повторно в другом приложении, поскольку его слишком тяжело выпутать из текущего приложения. (Неподвижность, Immobility).

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

Причины плохого дизайна

Что делает дизайн жестким, хрупким и неподвижным? Взаимозависимость модулей.

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

Хрупкость (fragility) это склонность системы к поломкам во множестве мест после единственного изменения. Обычно новые проблемы происходят в местах, концептуально не связанных с местом изменений. Такая хрупкость серьезно подрывает веру в дизайн и сопровождение системы. Пользователи и менеджеры не могут предсказать качества их продукта. Простые изменения в одной части приложения приводят к ошибкам в других, совершенно несвязанных частях. Исправление этих ошибок приводит к еще большему количеству проблем, и процесс сопровождения превращается в известного пса, гоняющегося за собственным хвостом.

Дизайн является неподвижным (immobile), когда нужные части системы сильно завязаны на другие нежелательные подробности. Чтобы представить проектировщику, насколько легко использовать существующий дизайн повторно, достаточно подумать о том, насколько просто его будет использовать в новом приложении. Если дизайн является сильносвязанным, то этот проектировщик ужаснется количеством работы, необходимой для отделения требуемых частей системы от ненужных подробностей. В большинстве случаев, такой дизайн не является повторно используемым, поскольку стоимость его отделения превышает разработку его с нуля.

---------------------------------------------------------

Сегодня все описанное здесь может показаться откровенным баяном, и так оно и есть, ведь оригинал статьи, чей перевод вы только что прочитали, написан 16 (!) лет назад. Тем не менее она остается весьма актуальной и сегодня.

За это время многое изменилось, и теперь мы все реже сталкиваемся с кривым структурным дизайном, стараясь сделать из него объектно-ориентированную конфетку. Вместо этого, мы все чаще и чаще сталкиваемся с ОО-дизайном, который является жестким, хрупким и неподвижным, и думаем, где же мы сбились со своего пути. Мы выучили десятки принципов разработки, но стали забывать, ради чего мы их хотели использовать.

Здесь подняты очень хорошие вопросы. Чем опасны хрупкие и жесткие системы? Да тем, что процесс управления подобным проектом становится непредсказуемым и, по сути, неуправляемым. Как менеджер может давать, или не давать добро на добавление некоторой фичи, если он не знает, сколько на самом деле на это потребуется времени? А как выплачивать тот самый технический долг, когда при его выплате мы огребем, причем понять сколько именно огребем мы не можем, пока не огребем?

Проблема с повторным использованием тоже очень актуальна. Постоянные читатели должны помнить, что в моем понимании юнит-тесты служат не только для проверки некоторых предположений относительно тестируемого модуля, но и для определения степени его связанности и могут служить показателем повторного использования. Мысли у Боба Мартина аналогичны (хоть и в более широком контексте): для того, чтобы понять, будет ли использоваться ваш код повторно нужно, чтобы трудозатраты на его повторное использование были меньшими, чем стоимость разработки с нуля. В противном случае никто с этим делом не будет даже заморачиваться.

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

Помогла статья? Оцените её!
0 из 5. Общее количество голосов - 0
 

You have no rights to post comments

Дмитрий Крикунов

Публикую статьи, обучающие курсы и новости по программированию: алгоритмам, языкам (С++, Java), параллельному программированию, паттернам и библиотекам (Qt, boost).