Принцип подстановки Лисков
Принцип подстановки Лисков (англ. Liskov Substitution Principle, LSP) — принцип организации подтипов в объектно-ориентированном программировании, предложенный Барбарой Лисков в 1987 году[1][2]: если является свойством, верным относительно объектов некоторого типа , тогда также должно быть верным для объектов типа , где является подтипом типа .
Более популярна интерпретация Роберта Мартина[3]: функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.
Таким образом, идея Лисков о «подтипе» даёт определение понятия замещения — если является подтипом , тогда объекты типа в программе могут быть замещены объектами типа без каких-либо изменений желательных свойств этой программы.
Этот принцип является важнейшим критерием для оценки качества принимаемых решений при построении иерархий наследования. Сформулировать его можно в виде простого правила: тип будет подтипом тогда и только тогда, когда каждому объекту типа соответствует некий объект типа таким образом, что для всех программ , реализованных в терминах , поведение не будет меняться, если заменить на . Иными словами, поведение наследующих классов не должно противоречить поведению, заданному базовым классом, то есть поведение наследующих классов должно быть ожидаемым для кода, использующего переменную базового типа.
Саттер и Александреску в отношении C++ интерпретируют принцип таким образом: подкласс не должен требовать от вызывающего кода больше, чем базовый класс, и не должен предоставлять вызывающему коду меньше, чем базовый класс. По их мнению, публичное наследование в C++ можно употреблять только тогда, когда оно удовлетворяет принципу Лисков, тогда как приватное наследование можно использовать для доступа к protected-части базы и перекрытия виртуальных методов. В любом же ином случае, то есть для всего лишь повторного использования кода из базы, наследование применять нельзя.
Основания: использование публичного наследования для повторного использования кода приводит к тому, что внешний мир начинает считать класс Derived разновидностью класса Base, и возможно появление кода, явно использующего этот факт. Это сильно сужает простор для манёвра архитектора в дальнейшем поддержании и рефакторинге класса Derived.
Принцип используется как составная часть методологии SOLID (буква «L» в аббревиатуре).
Проектирование по контракту[править | править код]
Принцип подстановки (замещения) Лисков имеет близкое отношение к методологии контрактного программирования, и ведёт к некоторым ограничениям на то, как контракты могут взаимодействовать с наследованием:
- предусловия не могут быть усилены в подклассе;
- постусловия не могут быть ослаблены в подклассе;
- исторические ограничения («правило истории») — подкласс не должен создавать новых мутаторов свойств базового класса, если базовый класс не предусматривал методов для изменения определённых в нём свойств, подтип этого класса так же не должен создавать таких методов.
Иными словами правило истории требует, чтобы неизменяемые данные базового класса не должны быть изменяемыми в подклассе. Данная концепция, представленная Лисков и Винг, являлась новаторской для теории программной архитектуры.
Также принцип LSP подразумевает, что методы подкласса не могут генерировать никаких дополнительных исключений, кроме тех, которые сами являются подклассами исключений, генерируемых методами надкласса (ковариантность и контравариантность).
Функция, использующая иерархию классов с нарушениями принципа Лисков, помимо оперирования ссылкой на базовый класс, оказывается также вынуждена знать и о подклассе. Подобная функция нарушает принцип открытости/закрытости, поскольку она требует модификации в случае появления в системе новых производных классов.
В данном контексте принцип подстановки можно переформулировать следующим образом: функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.
Принцип Лисков заставляет задуматься о том, что такое «декларация типа» в терминах объектно-ориентированного языка программирования, который мы используем. Достаточно ли нам описать интерфейс объекта с помощью обычного абстрактного класса со списком методов, типами параметров и возвращаемого значения? Каким образом мы можем декларировать требования к значениям параметров метода и свойства, которыми будет обладать возвращаемое значение? Как нам описать исключения, которые может сгенерировать метод во время выполнения? Как нам описать изменение состояния объекта на разных этапах его жизненного цикла?
Задавая себе эти вопросы и находя ответы, можно спроектировать систему, которая действительно будет удовлетворять принципу подстановки Барбары Лисков.
Примечания[править | править код]
- ↑ Liskov, Barbara Data abstraction and hierarchy (4 октября 1987). Дата обращения: 23 марта 2008. Архивировано 30 июня 2019 года.
- ↑ Liskov, Barbara; Wing, Jeannette.: Behavioral Subtyping Using Invariants and Constraints (PS) (июль 1999). Дата обращения: 5 октября 2006. Архивировано 30 августа 2012 года.
- ↑ Martin, Robert The Liskov Substitution Principle (PS). Дата обращения: 5 октября 2006. Архивировано 30 августа 2012 года.