Разберите этот Java‑пример: class Rectangle { int width, height; int area() { return width*height; } } class Square extends Rectangle { void setSide(int s){width=height=s;} } Обсудите, нарушает ли наследование Liskov Substitution Principle и как лучше спроектировать иерархию

26 Мая в 10:03
12 +1
0
Ответы
1
Коротко: да — в типичном (изменяемом) варианте это нарушает Liskov Substitution Principle (LSP). Почему и как лучше спроектировать — ниже.
Почему нарушает LSP
- У Rectangle клиент может ожидать возможность менять ширину и высоту независимо (например методы setWidth, setHeight или прямой доступ к полям).
- Square вынужденно поддерживает инвариант width == height, поэтому поведение при независимом изменении сторон меняется и нарушает ожидания клиента.
Пример нарушения (иллюстрация поведения):
- Ожидание клиента: если сделать
- setWidth(222), затем setHeight(333), то area должна быть 2⋅3=62\cdot 3=623=6.
- Для Square (который при каждом сетте ставит обе стороны равными) реальный результат будет, например, 3⋅3=93\cdot 3=933=9 (или 2⋅2=42\cdot 2=422=4 в зависимости от порядка). Это несоответствие ожиданию — LSP нарушен.
Как лучше спроектировать
1) Отделить интерфейс поведения от конкретных реализаций
- Ввести интерфейс Shape (или Rectangular) с методом area():
- class Rectangle implements Shape { ... }
- class Square implements Shape { ... }
- Тогда Square не наследует мутабельное поведение Rectangle и не ломает ожидания.
2) Сделать объекты неизменяемыми (value objects)
- Сделать Rectangle immutable (только конструктор + геттеры, без setWidth/setHeight). Тогда Square может наследовать Rectangle безопасно:
- class Rectangle { final int width,height; Rectangle(int w,int h){...} int area(){ return width*height; } }
- class Square extends Rectangle { Square(int s){ super(s,s); } }
- При отсутствии мутаторов подкласс не ломает контракт — LSP соблюдается.
3) Композиция/фабрики вместо наследования
- Если нужно представлять «прямоугольник, который может быть квадратом» — использовать фабрику: Rectangle.ofSquare(side) возвращает Rectangle с равными сторонами, но не наследовать Square от Rectangle.
Резюме (рекомендация)
- Если класс Rectangle предоставляет мутабельные методы изменения ширины/высоты — не делать Square наследником Rectangle. Используйте отдельные реализации интерфейса Shape или композицию.
- Если нужен подкласс Square, делайте объекты immutable (тогда наследование безопасно).
26 Мая в 10:08
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир