Что такое принцип подстановки Барбары Лисков (LSP)?

Ответ

Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) — это один из пяти принципов SOLID. Он гласит, что объекты производного (дочернего) класса должны быть способны заменять объекты базового (родительского) класса без изменения корректности работы программы.

Простыми словами: если у вас есть функция, которая работает с классом A, она должна так же корректно работать с любым его подклассом B, не зная о существовании B.

Почему это важно? LSP гарантирует, что иерархия наследования является семантически правильной. Подкласс должен расширять, а не изменять поведение базового класса.

Классический пример нарушения LSP (проблема квадрата и прямоугольника):

class Rectangle:
    def __init__(self, width, height):
        self._width = width
        self._height = height

    def set_width(self, width):
        self._width = width

    def set_height(self, height):
        self._height = height

    def get_area(self):
        return self._width * self._height

# Квадрат — это частный случай прямоугольника, но...
class Square(Rectangle):
    def __init__(self, side):
        super().__init__(side, side)

    # Нарушение: изменение ширины влияет на высоту, что неверно для базового класса
    def set_width(self, width):
        self._width = width
        self._height = width

    # Аналогичное нарушение
    def set_height(self, height):
        self._width = height
        self._height = height

# Функция, которая ожидает поведение Rectangle
def process_rectangle(rect: Rectangle):
    rect.set_width(5)
    rect.set_height(10)
    # Ожидаем площадь 5 * 10 = 50
    print(f"Ожидаемая площадь: 50, Фактическая: {rect.get_area()}")

# С базовым классом всё работает
process_rectangle(Rectangle(2, 3)) # Фактическая: 50

# С подклассом поведение нарушается
process_rectangle(Square(2))      # Фактическая: 100 (т.к. set_height изменила и ширину)

Этот код нарушает LSP, потому что Square изменяет инвариант (независимость ширины и высоты), установленный в Rectangle. Это приводит к непредсказуемым результатам.