Ответ
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) — это третий из пяти принципов SOLID. Он гласит:
Объекты базового класса должны быть заменяемыми объектами его подклассов без изменения корректности программы.
Это означает, что наследующий класс должен:
- Полностью реализовывать контракт (интерфейс) родительского класса.
- Не ужесточать предусловия (требования к входным данным).
- Не ослаблять постусловия (гарантии на результат и состояние).
- Не изменять инварианты класса (условия, всегда истинные для объекта).
- Не выбрасывать новых исключений, не являющихся подтипами исключений базового метода.
Классический пример нарушения LSP (Квадрат/Прямоугольник):
// Базовый класс
class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
// Подкласс, нарушающий LSP
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // Побочный эффект: меняет и высоту!
}
@Override
public void setHeight(int height) {
this.height = height;
this.width = height; // Побочный эффект: меняет и ширину!
}
}
// Клиентский код, рассчитанный на работу с Rectangle
public void resizeRectangle(Rectangle r) {
// Предполагается, что ширина и высота меняются независимо
r.setWidth(5);
r.setHeight(4);
assert r.getArea() == 20; // Утверждение будет ЛОЖНЫМ для Square!
// Для Square после setHeight(4) ширина тоже станет 4, и площадь будет 16.
}
Почему это нарушение? Клиентский код resizeRectangle ожидает, что setWidth и setHeight работают независимо, как определено в Rectangle. Класс Square нарушает это поведение, внося скрытые побочные эффекты. Замена Rectangle на Square приводит к ошибке.
Следствие LSP: Наследование должно описывать отношение «является» (is-a) не только с точки зрения данных, но и с точки зрения поведения. Если подкласс не может выполнять все обязанности базового класса, не внося изменений в ожидаемое поведение, наследование применять не следует. Часто проблему решает использование композиции или выделение общего интерфейса.
Ответ 18+ 🔞
Да ты послушай, какой принцип эти программисты выдумали, блядь! Подстановка Барбары Лисков, ёпта! Звучит, как название какого-то ебучьего детектива, а на деле — простая мысль, до которой некоторые, блядь, своим ебалом дойти не могут.
Суть в чём, в рот меня чих-пых: если у тебя есть класс-родитель и класс-ребёнок, то ты должен спокойно подсунуть этого ребёнка везде, где ждут родителя, и всё должно работать, а не разъебываться в пизду. Не должно быть сюрпризов, типа «ой, а я ещё и не то сделал».
Вот смотри, классический пиздец, который все приводят — квадрат и прямоугольник. Вроде бы логично: квадрат — это прямоугольник, у которого все стороны равны. Ан нет, блядь!
class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int getArea() { return width * height; }
}
Всё чинно, благородно. Прямоугольник. Ширину поставил — ширина поменялась. Высоту поставил — высота поменялась. Красота.
А теперь его сынок, квадратик:
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // А вот и хуйня, блядь!
}
@Override
public void setHeight(int height) {
this.height = height;
this.width = height; // И тут та же самая хуйня!
}
}
Ну вроде бы правильно, ёпта! Квадрат же! Поставил ширину — и высота автоматом такая же. Математически-то верно!
А теперь представь, есть у тебя функция, которая работает с прямоугольниками:
public void resizeRectangle(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert r.getArea() == 20; // Ожидаем 5 * 4 = 20
}
Ты, такой довольный, суёшь туда свой Square. И что происходит? Блядь, волнение ебать!
setWidth(5)— ширина 5, и высота ТОЖЕ становится 5, сука!setHeight(4)— высота 4, и ширина ТОЖЕ становится 4, ёбаный насос!- В итоге у тебя и ширина, и высота равны 4. Площадь — 16. А твой
assertждал 20. ПИЗДЕЦ. Программа пошла по пизде, потому что квадратик нарушил негласный договор: «методsetWidthменяет ТОЛЬКО ширину».
Вот это и есть нарушение принципа Лисков, блядь! Наследник внаглую меняет ожидаемое поведение родителя. Клиентский код, рассчитанный на родителя, ломается о наследника.
Мораль сей басни, ёпта: наследование — это не про данные («у квадрата четыре угла, как и у прямоугольника»), а в первую очередь про ПОВЕДЕНИЕ, блядь! Если твой наследник не может честно выполнять все контракты родителя, не подкладывая свинью в виде скрытых действий — не лезь в наследование, иди нахуй. Используй композицию, интерфейсы, что угодно. Чтобы не было потом таких вот ебушки-воробушки, когда подмена одного объекта другим приводит к тихому, ебучему краху.