Что такое принцип подстановки Лисков (LSP) и насколько сложно его соблюдать на практике?

«Что такое принцип подстановки Лисков (LSP) и насколько сложно его соблюдать на практике?» — вопрос из категории ООП, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Сложности соблюдения LSP: Соблюдать LSP сложно, когда наследование используется для повторного использования кода, а не для выражения истинного отношения "является" (is-a).

Классический пример нарушения LSP:

class Rectangle {
    private int width;
    private int height;

    public void setWidth(int w) { this.width = w; }
    public void setHeight(int h) { this.height = h; }
    public int area() { return width * height; }
}

class Square extends Rectangle {
    @Override
    public void setWidth(int w) {
        super.setWidth(w);
        super.setHeight(w); // Нарушение контракта Rectangle!
    }
    // Аналогично переопределяется setHeight
}

Клиентский код, ожидающий, что setWidth и setHeight работают независимо, сломается при работе с Square.

Как соблюдать LSP на практике:

  1. Четкий контракт: Базовый класс должен явно или неявно определять инварианты, пред- и постусловия для своих методов.
  2. Предпочитать композицию: Если семантика подкласса отличается, лучше использовать композицию (Square содержит Rectangle).
  3. Избегать усиления предусловий и ослабления постусловий в подклассах.
  4. Не изменять типы возвращаемых значений или исключений непредсказуемым образом.

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