Чистая архитектура. Искусство разработки программного обеспечения - стр. 33
>dest
. Компания Acme – крупнейшая компания такси в нашем регионе, и бывшая жена президента компании Acme вышла замуж за президента нашей компании, и… В общем, вы поняли. Что может произойти с архитектурой нашей системы?Очевидно, мы должны бы добавить особый случай. Запрос с заказом для любого водителя из Acme должен бы конструироваться в соответствии с иным набором правил, чем для всех остальных.
Решить поставленную задачу проще всего простым добавлением инструкции if в модуль, занимающийся пересылкой заказов:
>if (driver.getDispatchUri(). startsWith("acme.com"))…
Конечно, ни один архитектор, дорожащий своей репутацией, не позволил бы добавить такую конструкцию в систему. Появление слова «acme» непосредственно в коде создает возможность появления самых разных неприятностей, не говоря уже о бреши в безопасности.
Например, представьте, что компания Acme добилась большого успеха, купила компанию Purple Taxi и объединенная компания решила сменить имя и адрес веб-сайта и объединить все системы оригинальных компаний. Получается, что теперь мы должны добавить еще одну инструкцию if для «purple»?
Архитектор должен изолировать систему от ошибок, подобных этой, и добавить модуль, управляющий созданием команд доставки заказов в соответствии с параметрами, указанным для URI в базе данных с настройками. Настройки могли бы выглядеть как-то так:
>UPI
>Acme.com
>Формат команды
>/pickupAddress/%s/pickupTime/%s/dest/%s
>UPI
>*.*
>Формат команды
>/pickupAddress/%s/pickupTime/%s/destination/%s
В результате архитектор вынужден добавить важный и сложный механизм из-за того, что интерфейсы не всех REST-служб оказались совместимыми.
Заключение
Принцип подстановки Барбары Лисков может и должен распространяться до уровня архитектуры. Простое нарушение совместимости может вызвать загрязнение архитектуры системы значительным количеством дополнительных механизмов.
10. Принцип разделения интерфейсов
Происхождение названия принципа разделения интерфейсов (Interface Segregation Principle; ISP) наглядно иллюстрирует схема на рис. 10.1.
Рис. 10.1. Принцип разделения интерфейсов
В данной ситуации имеется несколько классов, пользующихся операциями в классе >OPS
. Допустим, что >User1
использует только операцию >op1
, >User2
– только >op2
и >User3
– только >op3
.
Теперь представьте, что >OPS
– это класс, написанный на таком языке, как Java. Очевидно, что в такой ситуации исходный код >User1
непреднамеренно будет зависеть от >op2
и >op3
, даже при том, что он не пользуется ими. Эта зависимость означает, что изменения в исходном коде метода >op2
в классе >OPS
потребуют повторной компиляции и развертывания класса >User1
, несмотря на то что для него ничего не изменилось.
Эту проблему можно решить разделением операций по интерфейсам, как показано на рис. 10.2.
Рис. 10.2. Разделение операций
Если снова представить, что этот интерфейс реализован на языке со строгим контролем типов, таком как Java, исходный код >User1
будет зависеть от >U1Ops
и >op1
, но не от >OPS
. То есть изменения в >OPS
, которые не касаются >User1
, не потребуют повторной компиляции и развертывания >User1
.
Принцип разделения интерфейсов и язык
Очевидно, что описание выше в значительной степени зависит от типа языка. Языки со статическими типами, такие как Java, вынуждают программистов создавать объявления, которые должны импортироваться или подключаться к исходному коду пользователя как-то иначе. Именно эти инструкции подключения в исходном коде пользователя создают зависимости и вынуждают выполнять повторную компиляцию и развертывание.