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

В 1988 году Барбара Лисков написала следующие строки с формулировкой определения подтипов.
Здесь требуется что-то вроде следующего свойства подстановки: если для каждого объекта o1 типа S существует такой объект o2 типа T, что для всех программ P, определенных в терминах T, поведение P не изменяется при подстановке o1 вместо o2, то S является подтипом T [24].
Чтобы понять эту идею, известную как принцип подстановки Барбары Лисков (Liskov Substitution Principle; LSP), рассмотрим несколько примеров.
Руководство по использованию наследования
Представьте, что у нас есть класс с именем
LicensecalcFee()BillingLicense:PersonalLicenseBusinessLicense
Рис. 9.1. Класс License и его производные, соответствующие принципу LSP
Этот дизайн соответствует принципу подстановки Барбары Лисков, потому что поведение приложения
BillingLicenseПроблема квадрат/прямоугольник
Классическим примером нарушения принципа подстановки Барбары Лисков может служить известная проблема квадрат/прямоугольник (рис. 9.2).

Рис. 9.2. Известная проблема квадрат/прямоугольник
В этом примере класс
SquareRectangleUserRectangleRectangle r =…r. setW(5);r. setH(2);assert(r.area() == 10);Если на место… подставить код, создающий экземпляр
SquareassertUserUserLSP и архитектура
На заре объектно-ориентированной революции принцип LSP рассматривался как руководство по использованию наследования, как было показано в предыдущих разделах. Но со временем LSP был преобразован в более широкий принцип проектирования программного обеспечения, который распространяется также на интерфейсы и реализации.
Подразумеваемые интерфейсы могут иметь множество форм. Это могут быть интерфейсы в стиле Java, реализуемые несколькими классами. Или это может быть группа классов на языке Ruby, реализующих методы с одинаковыми сигнатурами. Или это может быть набор служб, соответствующих общему интерфейсу REST.
Во всех этих и многих других ситуациях применим принцип LSP, потому что существуют пользователи, зависящие от четкого определения интерфейсов и замещаемости их реализаций.
Лучший способ понять значение LSP с архитектурной точки зрения – посмотреть, что случится с архитектурой системы при нарушении принципа.
Пример нарушения 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. Они должны единообразно интерпретировать поля
pickupAddresspickupTimedestinationТеперь предположим, что компания такси Acme наняла несколько программистов, которые ознакомились со спецификацией недостаточно внимательно. Они сократили имя поля
destinationdestОчевидно, мы должны бы добавить особый случай. Запрос с заказом для любого водителя из Acme должен бы конструироваться в соответствии с иным набором правил, чем для всех остальных.
Решить поставленную задачу проще всего простым добавлением инструкции if в модуль, занимающийся пересылкой заказов:
if (driver.getDispatchUri(). startsWith("acme.com"))…Конечно, ни один архитектор, дорожащий своей репутацией, не позволил бы добавить такую конструкцию в систему. Появление слова «acme» непосредственно в коде создает возможность появления самых разных неприятностей, не говоря уже о бреши в безопасности.