Чтение онлайн

на главную - закладки

Жанры

Чистая архитектура. Искусство разработки программного обеспечения
Шрифт:

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

Принцип разделения интерфейсов и архитектура

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

Рассмотрим, например, действия архитектора,

работающего над системой S. Он пожелал включить в систему некоторый фреймворк F. Теперь представьте, что авторы F связали его с поддержкой конкретной базы данных D. То есть S зависит от F, который зависит от D (рис. 10.3).

Рис. 10.3. Проблемная архитектура

Теперь представьте, что D включает функции, которые не используются фреймворком F и, соответственно, не используются системой S. Изменения в этих функциях внутри D могут вынудить повторно развернуть F и, соответственно, повторно развернуть S. Хуже того, ошибка в одной из таких функций внутри D может спровоцировать появление ошибок в F и S.

Заключение

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

Мы развернем эту мысль подробнее при обсуждении принципа совместного использования (Common Reuse Principle; CRP) в главе 13 «Связность компонентов».

11. Принцип инверсии зависимости

Принцип инверсии зависимости (Dependency Inversion Principle; DIP) утверждает, что наиболее гибкими получаются системы, в которых зависимости в исходном коде направлены на абстракции, а не на конкретные реализации.

В языках со статической системой типов, таких как Java, это означает, что инструкции

use
,
import
и
include
должны ссылаться только на модули с исходным кодом, содержащим интерфейсы, абстрактные классы и другие абстрактные объявления. Никаких зависимостей от конкретных реализаций не должно быть.

То же правило действует для языков с динамической системой типов, таких как Ruby или Python. Исходный код не должен зависеть от модулей с конкретной реализацией. Однако в этих языках труднее определить, что такое конкретный модуль. В частности, это любой модуль, в котором реализованы вызываемые функции.

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

String
в Java – это конкретный класс и его невозможно сделать абстрактным. Зависимости исходного кода от конкретного модуля
java.lang.string
невозможно и не нужно избегать.

С другой стороны, класс

String
очень стабилен. Изменения в этот класс вносятся крайне редко и жестко контролируются. Программистам и архитекторам не приходится беспокоиться о частых и непредсказуемых изменениях в
String
.

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

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

активно разрабатываться и претерпевают частые изменения.

Стабильные абстракции

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

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

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

 Не ссылайтесь на изменчивые конкретные классы. Ссылайтесь на абстрактные интерфейсы. Это правило применимо во всех языках, независимо от устройства системы типов. Оно также накладывает важные ограничения на создание объектов и определяет преимущественное использование шаблона «Абстрактная фабрика».

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

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

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

Фабрики

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

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

Рисунок 11.1 демонстрирует, как работает такая схема. Приложение

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

Поделиться:
Популярные книги

Черный дембель. Часть 2

Федин Андрей Анатольевич
2. Черный дембель
Фантастика:
попаданцы
альтернативная история
4.25
рейтинг книги
Черный дембель. Часть 2

Кодекс Крови. Книга ХIV

Борзых М.
14. РОС: Кодекс Крови
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Крови. Книга ХIV

Магия чистых душ 3

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Магия чистых душ 3

Жестокая свадьба

Тоцка Тала
Любовные романы:
современные любовные романы
4.87
рейтинг книги
Жестокая свадьба

Усадьба леди Анны

Ром Полина
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Усадьба леди Анны

Измена. Возвращение любви!

Леманн Анастасия
3. Измены
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Измена. Возвращение любви!

Два лика Ирэн

Ром Полина
Любовные романы:
любовно-фантастические романы
6.08
рейтинг книги
Два лика Ирэн

Опасная любовь командора

Муратова Ульяна
1. Проклятые луной
Фантастика:
фэнтези
5.00
рейтинг книги
Опасная любовь командора

Газлайтер. Том 8

Володин Григорий
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 8

Мымра!

Фад Диана
1. Мымрики
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Мымра!

Таня Гроттер и магический контрабас

Емец Дмитрий Александрович
1. Таня Гроттер
Фантастика:
фэнтези
8.52
рейтинг книги
Таня Гроттер и магический контрабас

Имперский Курьер

Бо Вова
1. Запечатанный мир
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Имперский Курьер

Страж. Тетралогия

Пехов Алексей Юрьевич
Страж
Фантастика:
фэнтези
9.11
рейтинг книги
Страж. Тетралогия

Сердце дракона. Танец с врагом

Серганова Татьяна
2. Танец с врагом
Любовные романы:
любовно-фантастические романы
5.25
рейтинг книги
Сердце дракона. Танец с врагом