Эта статья является частью серии SOLID, в которой подробно описаны все продукты S.O.L.I.D. принципы, включая примеры, передовой опыт, каковы распространенные нарушения и почему это приносит пользу разработчику и самому продукту.

Принцип замещения Лисков может быть самым сложным принципом аббревиатуры SOLID, потому что он имеет дело с семантикой и требованиями подтипа.

Определение

Если S является подтипом T, то объекты типа T могут быть заменены объектами типа S (т. е. объект типа T может быть замещен любым объектом типа подтип S) без изменения каких-либо желаемых свойств T (правильность, выполняемая задача и т. д.).

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

Согласно определению и другим статьям, мы также можем добавить следующее предложение:

Требование к подтипу: пусть f(x) будет доказуемым свойством объектов x типа T. Тогда f(y) должно быть истинным для объектов y типа S, где S является подтипом T.

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

Распространенные ошибки и плохая практика

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

Основная ошибка, которую совершает большинство разработчиков, заключается в том, что они путают наследование в реальной жизни («is a») с наследованием в коде, что приводит к плохой практике кодирования, большому количеству особых случаев в вашем коде (если такие утверждения, как «if object is A do то, иначе сделай то»). Чтобы не ошибиться, код будет работать так, как ожидается, но дизайн и обслуживание кода будут проблематичными.

Квадратно-прямоугольный корпус

Этот случай описывает тот факт, что квадрат в реальной жизни «является» прямоугольником, но имеет (четыре) равные стороны. Однако в программировании мы не можем распространить это поведение на квадрат, потому что оно в первую очередь изменяет предполагаемое поведение прямоугольного объекта.

В следующем примере мы видим, что Square расширяет класс Rectangle и изменяет ширину и высоту для каждого сеттера, что неверно.

Мы видим, что описанная выше проблема возникает, когда мы пытаемся создать некоторые модульные тесты:

Мы видим, что в приведенном выше примере функция testDimensions принимает объект Rectangle и проверяет его основные предположения. Из приведенных выше тестов только функция testRectangle пройдет все тесты, а testSquare завершится ошибкой.

Здесь мы видим хороший пример принципа подстановки Лисков и его нарушения. Чтобы решить вышеуказанную проблему, хорошим решением является создание абстракции Rectangle и Square в качестве общего поведения и перепроектирование этих классов для расширения общего поведения.

Выводы

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

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

Дальнейшее чтение

Если вам понравилась статья, дайте мне знать, нажав кнопку «Рекомендация», или даже оставьте комментарий для обсуждения. Не стесняйтесь делиться и обсуждать свои подходы :-)