Чистая архитектура. Искусство разработки программного обеспечения - страница 33



 Не переопределяйте конкретные функции. Конкретные функции часто требуют зависимостей в исходном коде. Переопределяя такие функции, вы не устраняете эти зависимости – фактически вы наследуете их. Для управления подобными зависимостями нужно сделать функцию абстрактной и создать несколько ее реализаций.

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

Фабрики

Чтобы соблюсти все эти правила, необходимо предусмотреть особый способ создания изменчивых объектов. Это объясняется тем, что практически во всех языках создание объектов связано с образованием зависимостей на уровне исходного кода от конкретных определений этих объектов.

В большинстве объектно-ориентированных языков, таких как Java, для управления подобными нежелательными зависимостями можно использовать шаблон «Абстрактная фабрика».

Рисунок 11.1 демонстрирует, как работает такая схема. Приложение >Application использует конкретную реализацию >ConcreteImpl через интерфейс >Service. Однако приложению требуется каким-то образом создавать экземпляры >ConcreteImpl. Чтобы решить эту задачу без образования зависимости от >ConcreteImpl на уровне исходного кода, приложение вызывает метод >makeSvc интерфейса фабрики >ServiceFactory. Этот метод реализован в классе >ServiceFactoryImpl, наследующем >ServiceFactory. Эта реализация создает экземпляр >ConcreteImpl и возвращает его как экземпляр интерфейса >Service.


Рис. 11.1. Использование шаблона «Абстрактная фабрика» для управления зависимостями


Извилистая линия на рис. 11.1 обозначает архитектурную границу. Она отделяет абстракцию от конкретной реализации. Все зависимости в исходном коде пересекают эту границу в одном направлении – в сторону абстракции.

Извилистая линия делит систему на два компонента: абстрактный и конкретный. Абстрактный компонент содержит все высокоуровневые бизнес-правила приложения. Конкретный компонент содержит детали реализации этих правил.

Обратите внимание, что поток управления пересекает извилистую линию в направлении, обратном направлению зависимостей в исходном коде. Зависимости следуют в направлении, противоположном направлению потока управления – именно поэтому принцип получил название принципа инверсии зависимости.

Конкретные компоненты

Конкретный компонент >ConcreteImpl на рис. 11.1 имеет единственную зависимость, то есть он нарушает принцип DIP. Это нормально. Полностью устранить любые нарушения принципа инверсии зависимости невозможно, но их можно сосредоточить в узком круге конкретных компонентов и изолировать от остальной системы.

Большинство систем будет содержать хотя бы один такой конкретный компонент – часто с именем main, потому что включает функцию >main[25]. В схеме, изображенной на рис. 11.1, функция >main могла бы создавать экземпляр >ServiceFactoryImpl и сохранять ссылку на него в глобальной переменной типа >ServiceFactory. Благодаря этому приложение >Application сможет использовать данную глобальную переменную для обращения к фабрике.

Заключение

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