Название: Чистая архитектура. Искусство разработки программного обеспечения
Автор: Роберт Мартин
Издательство: Питер
Жанр: Программирование
Серия: Библиотека программиста (Питер)
isbn: 978-5-4461-0772-8, 9780134494166
isbn:
Рис. 9.1. Класс License и его производные, соответствующие принципу LSP
Этот дизайн соответствует принципу подстановки Барбары Лисков, потому что поведение приложения Billing
не зависит от использования того или иного подтипа. Оба подтипа могут служить заменой для типа License
.
Проблема квадрат/прямоугольник
Классическим примером нарушения принципа подстановки Барбары Лисков может служить известная проблема квадрат/прямоугольник (рис. 9.2).
Рис. 9.2. Известная проблема квадрат/прямоугольник
В этом примере класс Square
(представляющий квадрат) неправильно определен как подтип класса Rectangle
(представляющего прямоугольник), потому что высоту и ширину прямоугольника можно изменять независимо; а высоту и ширину квадрата можно изменять только вместе. Поскольку класс User
полагает, что взаимодействует с экземпляром Rectangle
, его легко можно ввести в заблуждение, как демонстрирует следующий код:
Rectangle r =…
r. setW(5);
r. setH(2);
assert(r.area() == 10);
Если на место… подставить код, создающий экземпляр Square
, тогда проверка assert
потерпит неудачу. Единственный способ противостоять такому виду нарушений принципа LSP – добавить в класс User
механизм (например, инструкцию if), определяющий ситуацию, когда прямоугольник фактически является квадратом. Так как поведение User
зависит от используемых типов, эти типы не являются заменяемыми (совместимыми).
LSP и архитектура
На заре объектно-ориентированной революции принцип LSP рассматривался как руководство по использованию наследования, как было показано в предыдущих разделах. Но со временем LSP был преобразован в более широкий принцип проектирования программного обеспечения, который распространяется также на интерфейсы и реализации.
Подразумеваемые интерфейсы могут иметь множество форм. Это могут быть интерфейсы в стиле Java, реализуемые несколькими классами. Или это может быть группа классов на языке Ruby, реализующих методы с одинаковыми сигнатурами. Или это может быть набор служб, соответствующих общему интерфейсу REST.
Во всех этих и многих других ситуациях применим принцип LSP, потому что существуют пользователи, зависящие от четкого определения интерфейсов и замещаемости их реализаций.
Лучший способ понять значение LSP с архитектурной точки зрения – посмотреть, что случится с архитектурой системы при нарушении принципа.
Пример нарушения LSP
Допустим, что мы взялись за создание приложения, объединяющего несколько служб, предоставляющих услуги такси. Клиенты, как предполагается, будут использовать наш веб-сайт для поиска подходящего такси, независимо от принадлежности к той или иной компании. Как только клиент подтверждает заказ, наша система передает его выбранному такси, используя REST-службу.
Теперь предположим, что URI службы является частью информации, хранящейся в базе данных водителей. Выбрав водителя, подходящего СКАЧАТЬ