Пример нарушения LSP

Допустим, что мы взялись за создание приложения, объединяющего несколько служб, предоставляющих услуги такси. Клиенты, как предполагается, будут использовать наш веб-сайт для поиска подходящего такси, независимо от принадлежности к той или иной компании. Как только клиент подтверждает заказ, наша система передает его выбранному такси, используя REST-службу.

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

Допустим, что для водителя с именем Bob адрес URI отправки заказа выглядит так:


>purplecab.com/driver/Bob


Наша система добавит в конец этого URI информацию о заказе и пошлет его методом PUT:


>purplecab.com/driver/Bob

>/pickupAddress/24 Maple St.

>/pickupTime/153

>/destination/ORD


Это явно означает, что все службы должны соответствовать общему интерфейсу REST. Они должны единообразно интерпретировать поля >pickupAddress, >pickupTime и >destination.

Теперь предположим, что компания такси Acme наняла несколько программистов, которые ознакомились со спецификацией недостаточно внимательно. Они сократили имя поля >destination до >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