Какой принцип SOLID напрямую связан с корректным использованием наследования?

Ответ

С корректным использованием наследования напрямую связан принцип Liskov Substitution Principle (LSP) — "Принцип подстановки Барбары Лисков".

Формулировка: Объекты базового класса должны быть заменяемыми объектами его подклассов без изменения корректности программы. Наследующий класс должен дополнять, а не изменять поведение родителя.

Суть принципа: LSP устанавливает строгие правила для наследования. Если класс S является подтипом класса T, то везде, где используется T, можно безопасно использовать S, не ломая логику.

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

class Rectangle {
    protected int width, height;
    public void setWidth(int w) { width = w; }
    public void setHeight(int h) { height = h; }
    public int getArea() { return width * height; }
}

// Квадрат «является» прямоугольником с математической точки зрения,
// но с точки зрения поведения — нет.
class Square extends Rectangle {
    @Override
    public void setWidth(int w) {
        super.setWidth(w);
        super.setHeight(w); // Нарушение: изменение высоты при установке ширины
    }
    @Override
    public void setHeight(int h) {
        super.setHeight(h);
        super.setWidth(h); // Нарушение: изменение ширины при установке высоты
    }
}

// Код, который работает с Rectangle, сломается для Square
void testArea(Rectangle r) {
    r.setWidth(5);
    r.setHeight(4);
    assert r.getArea() == 20; // Утверждение провалится для Square (area=16)
}

Вывод: LSP требует, чтобы подклассы были семантически совместимы со своими суперклассами, а не только синтаксически. Наследование должно означать "является" в строгом поведенческом смысле.

Ответ 18+ 🔞

А, ну вот, подъехали мы к самому сочному, блядь! Принцип подстановки Лисков, ёпта! Это та самая Барбара, которая влепила нам по мозгам, что наследование — это вам не хуй собачий, а серьёзная хуйня.

Короче, суть в чём: если у тебя есть какой-то базовый класс, типа Утка, то его наследник, например, РезиноваяУтка, должен подходить везде, где используется Утка. И программа при этом не должна превращаться в пиздец. То есть, подставляешь наследника — и всё должно работать, как часы, а не как говно в проруби.

А теперь смотри, где все обламываются. Берём классический пример, который всех вгоняет в ступор.

Допустим, есть у нас класс Прямоугольник. Ну, прямоугольник и прямоугольник, хуле. Ширина, высота, площадь.

class Rectangle {
    protected int width, height;
    public void setWidth(int w) { width = w; }
    public void setHeight(int h) { height = h; }
    public int getArea() { return width * height; }
}

И тут приходит какой-то умник и говорит: «А давайте сделаем класс Квадрат! Он же является прямоугольником, похуй!». И тупо наследует.

class Square extends Rectangle {
    @Override
    public void setWidth(int w) {
        super.setWidth(w);
        super.setHeight(w); // ОЙ, БЛЯДЬ! А ЧЁ ЭТО ТЫ ТУТ ДЕЛАЕШЬ?
    }
    @Override
    public void setHeight(int h) {
        super.setHeight(h);
        super.setWidth(h); // ДА ТЫ ЧЁ, СОВСЕМ ОХУЕЛ?!
    }
}

И вроде бы логично, ёпта: у квадрата ширина всегда равна высоте. Но с точки зрения поведения — это пиздец, извините. Смотри, что происходит.

Есть у нас функция, которая работает с любым Прямоугольником. Она выставляет ширину 5, высоту 4 и ожидает площадь 20. Логично же, да?

void testArea(Rectangle r) {
    r.setWidth(5);
    r.setHeight(4);
    assert r.getArea() == 20; // Упс, для Square тут будет 16! Пиздец, assertion failed!
}

И вот тут нас накрывает медным тазом. Подсунули мы Квадрат вместо Прямоугольника, а он, сука, ведёт себя не так! Он меняет вторую сторону при установке первой. Нарушается контракт, ожидания, вся хуйня. Программа летит в тартарары.

Вывод, блядь, какой? LSP — это про то, что наследование должно быть семантически правильным. Недосточно просто впихнуть extends и переопределить методы. Надо, чтобы наследник не ломал логику родителя. Чтобы его можно было подставить, и все остались живы-здоровы, а не охуели от неожиданного поведения.

Если наследник начинает делать какие-то ебушки-воробушки, которых от него не ждали, — это не наследование, а пиздопроебибна. Делай тогда композицию, блядь, и не позорься.