← Назад к вопросам

Связан ли полиморфизм подтипов c принципом подстановки Лисков (Liskov substitution)

2.0 Middle🔥 271 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Полиморфизм подтипов и принцип подстановки Лисков

Прямая связь

Да, полиморфизм подтипов и принцип подстановки Лисков (LSP) связаны напрямую. LSP является фундаментальным правилом, которое определяет, когда полиморфизм подтипов работает корректно. Это не просто связанные концепции — LSP показывает, как безопасно использовать полиморфизм подтипов.

Полиморфизм подтипов

Полиморфизм подтипов — это способность объектов подтипа быть использованными там, где ожидается объект родительского типа:

class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

def make_animal_speak(animal: Animal):
    print(animal.speak())

make_animal_speak(Dog())   # Работает!
make_animal_speak(Cat())   # Работает!

Это классический полиморфизм подтипов — Dog и Cat могут использоваться вместо Animal.

Принцип подстановки Лисков (Liskov Substitution Principle)

LSP гласит: Объекты подклассов должны корректно заменять объекты базового класса, не нарушая ожидаемое поведение программы.

Другими словами, если класс S является подтипом класса T, то объекты типа T в программе могут быть заменены объектами типа S без каких-либо нежелательных эффектов.

Примеры нарушения LSP

# ПЛОХО — нарушение LSP
class Bird:
    def fly(self):
        return "Flying..."

class Penguin(Bird):
    def fly(self):
        raise NotImplementedError("Penguins can't fly")

def make_bird_fly(bird: Bird):
    return bird.fly()

make_bird_fly(Penguin())  # Крах! LSP нарушен

Проблема: Penguin не может полностью заменить Bird, потому что ожидаемое поведение нарушено.

# ХОРОШО — соблюдение LSP
class Bird:
    def move(self):
        pass

class FlyingBird(Bird):
    def move(self):
        return "Flying..."

class Penguin(Bird):
    def move(self):
        return "Swimming..."

def make_bird_move(bird: Bird):
    return bird.move()

make_bird_move(Penguin())  # Работает правильно

Теперь иерархия корректна — все птицы могут двигаться, но по-разному.

Как LSP укрепляет полиморфизм подтипов

Безопасность контрактов:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        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  # Нарушение LSP!

def process_rectangle(rect: Rectangle):
    rect.set_width(5)
    rect.set_height(4)
    assert rect.get_area() == 20  # Провалится для Square!

Этот пример показывает, что Square не может безопасно заменить Rectangle, потому что нарушает его контракт.

Взаимоотношение

Полиморфизм подтипов — это механизм. LSP — это правило, которое определяет, когда механизм работает правильно.

Без соблюдения LSP:

  • Полиморфизм становится опасным
  • Трудно предсказать поведение программы
  • Возникают неожиданные ошибки
  • Код становится хрупким и сложным в поддержке

Практический вывод

При проектировании иерархии классов нужно спрашивать себя: 'Может ли подкласс безопасно заменить родительский класс?' Если нет — нарушена LSP, и архитектура требует переработки.