Ключевое

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


(довольно мутные) Требования

  1. Контракт родительского класса - подкласс должен соблюдать контракт суперкласса (все публичные методы должны работать аналогично).
  2. Семантическое поведение - поведение методов подкласса должно быть согласовано с поведением методов суперкласса.
  3. Инварианты - подклассы должны поддерживать инварианты (неизменные состояния) суперкласса.
  4. Пост- и предусловия - подклассы не должны усиливать предусловия (условия до выполнения метода) и ослаблять постусловия (условия после выполнения метода) суперкласса.

Пример

class Rectangle {
public:
    virtual void setWidth(double width) {
        this->width = width;
    }
 
    virtual void setHeight(double height) {
        this->height = height;
    }
 
    double getArea() const {
        return width * height;
    }
 
protected:
    double width;
    double height;
};
 
class Square : public Rectangle {
public:
    void setWidth(double width) override {
        this->width = width;
        this->height = width;
    }
 
    void setHeight(double height) override {
        this->width = height;
        this->height = height;
    }
};

Проблема:

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