Какие знаешь принципы Барбары Лисков

Ответ

Принцип подстановки Барбары Лисков (LSP) гласит: подтипы должны быть заменяемыми на свои базовые типы без нарушения работы программы.

Основные идеи:

  1. Наследники не должны ужесточать предусловия (требования к входным данным).
  2. Наследники не должны ослаблять постусловия (гарантии на выходные данные).
  3. Наследники должны сохранять инварианты базового класса.

Пример нарушения 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+ 🔞

Да ты послушай, какой прикол с этим принципом подстановки Лисков! Это ж, блядь, как в армии: если ты сын генерала, ты должен уметь всё то же, что и папаша, а не подводить роту, сука, своими выебонами.

Короче, суть в том, что если у тебя есть какой-то базовый тип, то на его место можно впихнуть любой подтип, и программа не должна обосраться. Всё должно работать, как часы, а не как мартышлюшка с гранатой.

Вот смотри, основные правила, блядь:

  1. Нельзя ужесточать правила приёма. Это как если бы отец говорил: «Приходи домой до 12, сынок». А сын-наследник заявляет: «А я, пап, буду приходить только до 10, и ещё только в белых носках». Да ты чё, мудила? Программа, которая ждёт от тебя одного, на таком наследнике сломается, как яйцо об лоб.
  2. Нельзя ослаблять гарантии на выход. Папаша обещал: «Я верну тебе либо деньги, либо новый велосипед». А сынуля такой: «А я, бля, могу вернуть тебе ржавую покрышку и доброе слово». Ну ёпта, нахуй такой наследник нужен? Доверия к нему — ноль ебать.
  3. Нужно сохранять все внутренние законы семьи (инварианты). Если в семье заведено не срать на ковёр, то и наследник не должен этого делать. Всё просто, как три копейки.

А вот тебе пример, где всё пошло по пизде, прямо классика:

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! Волнение ебать, программа-то думает, что работает с одним, а на деле получила полупидора, который творит свою хуйню.

Вывод, блядь, простой, как палка: наследование — это не для того, чтобы переписывать отцовские заветы на свой лад. Это чтобы дополнять и усиливать, а не ломать всё к хуям собачьим. Если наследник начинает городить своё, отличное от родителя — это не наследник, а пидарас шерстяной, и ему не место в семейной иерархии классов.