Обобщая, эти советы можно объединить в один:
Не создавайте зависимостей от чего-либо неиспользуемого.
Диаграмма противоречий для определения связности компонентов
Возможно, вы уже заметили, что три принципа связности компонентов вступают в противоречие друг с другом. Принципы эквивалентности повторного использования (REP) и согласованного изменения (CCP) являются включительными: оба стремятся сделать компоненты как можно крупнее. Принцип повторного использования (CRP) — исключительный, стремящийся сделать компоненты как можно мельче. Задача хорошего архитектора — разрешить это противоречие.
На рис. 13.1 изображена диаграмма противоречий
[30], показывающая, как три принципа связности влияют друг на друга. Ребра на диаграмме описывают цену нарушения принципа на противоположной вершине.
Архитектор, уделяющий внимание только принципам REP и CRP, обнаружит, что простое изменение вовлекает слишком большое количество компонентов. С другой стороны, архитектор, уделяющий особое внимание принципам CCP и REP, вынужден будет выпускать слишком много ненужных версий.
Рис. 13.1. Диаграмма противоречий принципов связности
Хороший архитектор найдет в этом треугольнике противоречий золотую середину, отвечающую текущим нуждам разработчиков, а также подумает об изменениях, которые могут произойти в будущем. Например, на ранних этапах разработки проекта принцип CCP намного важнее, чем REP, удобство разработки важнее удобства повторного использования.
Вообще говоря, в начале разработки наибольшую важность имеет правая сторона треугольника, когда единственной жертвой является повторное использование. Но по мере развития и интеграции в другие проекты фокус начинает смещаться влево. То есть организация компонентов в проекте может изменяться с течением времени. Это больше связано с тем, как проект разрабатывается и используется, нежели с тем, что фактически этот проект делает.
Заключение
В прошлом мы смотрели на связность проще, чем предполагают принципы эквивалентности повторного использования (REP), согласованного изменения (CCP) и совместного повторного использования (CRP). Когда-то мы думали, что связность — это просто атрибут, что модуль выполняет одну и только одну функцию. Однако три принципа связности компонентов описывают намного более сложное многообразие. Выбирая классы для включения в компоненты, нужно учитывать противодействующие силы, связанные с удобством повторного использования и разработки. Поиск баланса этих сил, исходя из потребностей приложения, — непростая задача. Кроме того, баланс практически всегда постоянно смещается. То есть разбиение, считающееся удачным сегодня, может оказаться неудачным через год. Как следствие, состав компонентов почти наверняка будет изменяться с течением времени и смещением фокуса проекта с удобства разработки к удобству повторного использования.
Глава 14. Сочетаемость компонентов
Следующие три принципа определяют правила взаимоотношений между компонентами. И снова мы сталкиваемся с противоречиями между удобством разработки и логической организацией. Силы, воздействующие на архитектуру структуры компонентов, носят технический, политический и непостоянный характер.
Принцип ацикличности зависимостей
Циклы в графе зависимостей компонентов недопустимы.
Бывало ли у вас так, что вы целый день проработали, заставили что-то заработать, а на следующее утро, придя на работу, обнаружили, что опять ничего не работает? Почему это произошло? Потому что кто-то задержался на работе дольше вас и изменил что-то, от чего зависит ваш код! Я называю это «синдромом следующего утра».
«Синдром следующего утра» возникает, когда одни и те же файлы с исходным кодом правят сразу несколько разработчиков. В относительно небольших проектах, над которыми трудится малое количество разработчиков, эта проблема не доставляет особых хлопот. Но в крупных проектах с многочисленным коллективом следующее утро может превратиться в настоящий кошмар. Нередко проходят неделя за неделей, а команда все не в состоянии собрать стабильную версию проекта. Вместо этого разработчики правят и правят свой код, пытаясь заставить его работать с изменениями, сделанными кем-то другим.
За последние десятилетия было выработано два решения этой проблемы, и оба пришли из телекоммуникационной отрасли. Первое: «еженедельные сборки». И второе: соблюдение принципа ацикличности зависимостей (Acyclic Dependencies Principle; ADP).
Еженедельные сборки
Еженедельные сборки часто используются в проектах среднего размера. Это решение действует так: все разработчики работают независимо первые четыре дня в неделе. Они изменяют собственные копии кода и не заботятся об интеграции результатов своего труда в коллективную основу. Затем, в пятницу, они объединяют свои изменения и пытаются собрать систему.
Этот подход имеет замечательное преимущество, позволяя разработчикам работать в изоляции четыре дня из пяти. Недостаток, конечно же, — большие трудозатраты на интеграцию в пятницу.
К сожалению, с развитием проекта становится все сложнее завершить интеграцию в пятницу. Бремя интеграции продолжает расти, пока не начинает захватывать субботу. Нескольких таких суббот достаточно, чтобы разработчики пришли к выводу, что интеграция должна начинаться в четверг — так начало интеграции постепенно переползает ближе к середине недели.
Одновременно с уменьшением отношения продолжительности разработки к продолжительности интеграции снижается эффективность команды. В конечном итоге ситуация становится настолько удручающей, что разработчики и руководители проекта заявляют о необходимости перехода на двухнедельный цикл сборки. Это ослабляет проблему на какое-то время, но время, затрачиваемое на интеграцию, продолжает расти вместе с размерами проекта.
В конечном счете этот сценарий приводит к кризису. Для поддержания эффективности на высоком уровне график сборки должен постоянно удлиняться, но такое удлинение увеличивает риски. Интеграция и тестирование становятся все сложнее, а команда теряет преимущества, которые дает быстрая обратная связь.
Устранение циклических зависимостей
Решение этой проблемы заключается в разделении проекта на компоненты, которые могут выпускаться независимо. Компоненты становятся единицами работы, ответственность за которые можно возложить на одного разработчика или на небольшую группу. Когда разработчики добиваются работоспособности компонента, они выпускают новую версию для использования другими разработчиками. Они присваивают этой версии номер и помещают в каталог, доступный другим разработчикам. Затем продолжают разработку компонента, изменяя свои локальные копии. А все остальные используют выпущенную версию.