Ответ
Принцип подстановки Барбары Лисков (LSP) гласит: подтипы должны быть заменяемыми на свои базовые типы без нарушения работы программы.
Основные идеи:
- Наследники не должны ужесточать предусловия (требования к входным данным).
- Наследники не должны ослаблять постусловия (гарантии на выходные данные).
- Наследники должны сохранять инварианты базового класса.
Пример нарушения LSP:
class Rectangle {
constructor(public width: number, public height: number) {}
setWidth(width: number) { this.width = width; }
setHeight(height: number) { this.height = height; }
}
class Square extends Rectangle {
setWidth(width: number) {
this.width = width;
this.height = width; // Нарушение LSP, меняет поведение родителя
}
}
function testArea(shape: Rectangle) {
shape.setWidth(5);
shape.setHeight(4);
console.log(shape.width * shape.height); // Ожидается 20, но для Square будет 25
}
Вывод: Наследование должно дополнять, а не изменять логику базового класса.
Ответ 18+ 🔞
Да ты послушай, какой прикол с этим принципом подстановки Лисков! Это ж, блядь, как в армии: если ты сын генерала, ты должен уметь всё то же, что и папаша, а не подводить роту, сука, своими выебонами.
Короче, суть в том, что если у тебя есть какой-то базовый тип, то на его место можно впихнуть любой подтип, и программа не должна обосраться. Всё должно работать, как часы, а не как мартышлюшка с гранатой.
Вот смотри, основные правила, блядь:
- Нельзя ужесточать правила приёма. Это как если бы отец говорил: «Приходи домой до 12, сынок». А сын-наследник заявляет: «А я, пап, буду приходить только до 10, и ещё только в белых носках». Да ты чё, мудила? Программа, которая ждёт от тебя одного, на таком наследнике сломается, как яйцо об лоб.
- Нельзя ослаблять гарантии на выход. Папаша обещал: «Я верну тебе либо деньги, либо новый велосипед». А сынуля такой: «А я, бля, могу вернуть тебе ржавую покрышку и доброе слово». Ну ёпта, нахуй такой наследник нужен? Доверия к нему — ноль ебать.
- Нужно сохранять все внутренние законы семьи (инварианты). Если в семье заведено не срать на ковёр, то и наследник не должен этого делать. Всё просто, как три копейки.
А вот тебе пример, где всё пошло по пизде, прямо классика:
class Rectangle {
constructor(public width: number, public height: number) {}
setWidth(width: number) { this.width = width; }
setHeight(height: number) { this.height = height; }
}
class Square extends Rectangle {
setWidth(width: number) {
this.width = width;
this.height = width; // Нарушение LSP, меняет поведение родителя
}
}
Видишь эту подлянку? Квадрат наследуется от прямоугольника, логично вроде. Но он, хитрая жопа, переопределяет метод setWidth и заодно меняет высоту! Это же пиздец, а не замена.
Вот функция, которая работает с прямоугольником:
function testArea(shape: Rectangle) {
shape.setWidth(5);
shape.setHeight(4);
console.log(shape.width * shape.height); // Ожидается 20, но для Square будет 25
}
Она вызывает два метода по отдельности, рассчитывая на логику прямоугольника. А если ей подсунуть квадрат, он после первого вызова станет 5x5, а после второго — 4x4. И площадь будет 16, а не 20! Волнение ебать, программа-то думает, что работает с одним, а на деле получила полупидора, который творит свою хуйню.
Вывод, блядь, простой, как палка: наследование — это не для того, чтобы переписывать отцовские заветы на свой лад. Это чтобы дополнять и усиливать, а не ломать всё к хуям собачьим. Если наследник начинает городить своё, отличное от родителя — это не наследник, а пидарас шерстяной, и ему не место в семейной иерархии классов.