← Назад к вопросам

Приведи пример нарушения принципа Барбары Лисков

2.0 Middle🔥 141 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нарушение принципа Барбары Лисков (Liskov Substitution Principle)

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

Классический пример нарушения: Квадрат и Прямоугольник

Это самый известный пример нарушения LSP:

public 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;
    }
}

public 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;
    }
}

Почему это нарушение LSP?

public class AreaCalculator {
    public int calculateArea(Rectangle rect) {
        rect.setWidth(5);
        rect.setHeight(4);
        int area = rect.getArea();
        return area;
    }
}

Rectangle rect = new Rectangle();
AreaCalculator calc = new AreaCalculator();
System.out.println(calc.calculateArea(rect)); // 20

Rectangle square = new Square();
System.out.println(calc.calculateArea(square)); // 16 - НЕОЖИДАННЫЙ РЕЗУЛЬТАТ!

Проблема: Square нельзя безопасно использовать вместо Rectangle, потому что его поведение нарушает контракт базового класса.

Другие примеры нарушения LSP

Птица может летать? Нет!

public abstract class Bird {
    public abstract void fly();
}

public class Eagle extends Bird {
    @Override
    public void fly() {
        System.out.println("Орёл летит");
    }
}

public class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Пингвины не летают!");
    }
}

public void makeBirdFly(Bird bird) {
    bird.fly();
}

Нарушение: Не все птицы летают. Пингвин не должен наследовать Bird.

Выброс неожиданного исключения

public class PaymentProcessor {
    public void processPayment(int amount) {
        System.out.println("Платёж обработан: " + amount);
    }
}

public class SpecialPaymentProcessor extends PaymentProcessor {
    @Override
    public void processPayment(int amount) {
        if (amount < 100) {
            throw new IllegalArgumentException("Минимальная сумма 100");
        }
        super.processPayment(amount);
    }
}

PaymentProcessor processor = new SpecialPaymentProcessor();
processor.processPayment(50); // Выбросится неожиданное исключение!

Усиление предусловий

public class Logger {
    public void log(String message) {
        if (message != null) {
            System.out.println(message);
        }
    }
}

public class StrictLogger extends Logger {
    @Override
    public void log(String message) {
        if (message == null || message.isEmpty()) {
            throw new IllegalArgumentException("Не может быть пусто");
        }
        System.out.println(message);
    }
}

Logger logger = new StrictLogger();
logger.log(""); // Выбросится исключение!

Как исправить нарушения LSP?

Решение 1: Правильная иерархия классов

public abstract class Shape {
    abstract int getArea();
}

public class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public int getArea() {
        return width * height;
    }
}

public class Square extends Shape {
    private int side;

    public Square(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }
}

Решение 2: Разделение интерфейсов

public interface FlyingAnimal {
    void fly();
}

public interface SwimmingAnimal {
    void swim();
}

public class Eagle implements FlyingAnimal {
    @Override
    public void fly() {
        System.out.println("Орёл летит");
    }
}

public class Penguin implements SwimmingAnimal {
    @Override
    public void swim() {
        System.out.println("Пингвин плывёт");
    }
}

Ключевые признаки нарушения LSP

  • Проверка типов: if (obj instanceof SpecificClass)
  • Выброс неожиданных исключений в подклассе
  • Усиление предусловий или ослабление постусловий
  • Поведение подкласса отличается от базового в неожиданных ситуациях

Лучшие практики

  1. Контракт базового класса — строго соблюдай в подклассах
  2. Не выбрасывай новые исключения — только те, что объявлены в базовом классе
  3. Не усиливай предусловия — не добавляй новые проверки
  4. Не ослабляй постусловия — не снижай гарантии результата
  5. Используй интерфейсы — для логически связанных операций

Принцип LSP обеспечивает надежность кода и упрощает его тестирование.