Принцип LSP работает через интерфейсы, то есть то, что является общедоступным в объекте / значении. В случае с Python у вас есть общедоступные атрибуты и методы, и поэтому, если вы реализуете интерфейс, вы должны его придерживаться.
В вашем примере класс Switch
определяет интерфейс только относительно его атрибутов, то есть он имеет поля ip
и dc
. Поскольку вы вызываете super
в CiscoSwitch
и JuniperSwitch
, они оба имеют эти поля и поэтому реализуют «интерфейс» Switch
и могут заменить его.
Теперь давайте рассмотрим условия, которые вы упомянули:
- Предварительные условия не могут быть усилены в подтипе
Предположим, у вас есть интерфейс с методом, который окрашивает фигуру, и этот метод принимает только шестнадцатеричные цвета ("#123456"
) или цвета rgb ((123,124,125)
). Предположим, вы создали класс Rectangle, который реализует этот интерфейс, но принимает только цвета rgb. Вы усиливаете предварительное условие. Если у вас есть эта строка в вашем коде:
generic_shape.color("#123456")
Вы не можете заменить свой прямоугольник в нем, поэтому вы нарушаете LSP.
- Постусловия не могут быть ослаблены в подтипе
Классическим примером будет возвращение None
для метода, который не должен возвращать None
. Предположим, что в интерфейсе Shape у вас есть метод getSide
, и вы создаете класс Circle
, а при реализации этого метода вы возвращаете None
. Это нарушает ожидания вызывающего абонента и может вызвать неожиданную ошибку:
side = generic_shape.get_side() # suppose it is a circle
scaled_side = side * 2 # you get an error
- Инварианты супертипа должны быть сохранены в подтипе
Этот, на мой взгляд, более сложный, потому что его трудно сделать явным. Единственный пример, который я могу себе представить, это сохранение свойства не равным Null, предположим, что в вашем примере у вас есть Switch, который не использует IP (возможно, использует MAC-addr), если интерфейс Switch ожидает, что поле ip
не должно быть Нет, ваш MAC-Switch будет работать с этим интерфейсом.
Надеюсь, это поможет!