Python Senior
**Проблема прямоугольника** — это классический пример нарушения принципов ООП,
Ответы
**Проблема прямоугольника** — это классический пример нарушения принципов ООП, а именно **принципа подстановки Лисков (LSP)**. Её ещё называют **Rectangle–Square Problem**. --- ## Суть проблемы (кратко) Интуитивно кажется логичным сказать: > **Квадрат — это прямоугольник** Но в **ООП это приводит к логической ошибке**, потому что квадрат **ужесточает поведение** прямоугольника и ломает ожидания кода. --- ![Image](https://i.sstatic.net/dx9Vr.png) ![Image](https://miro.medium.com/v2/resize%3Afit%3A1200/1%2AxGrkDFtUydJ5TN_RdqLYaA.jpeg) --- ## Классический пример ### Базовый класс: Прямоугольник ```python class Rectangle: def __init__(self, width, height): self._width = width self._height = height def set_width(self, w): self._width = w def set_height(self, h): self._height = h def area(self): return self._width * self._height ``` --- ### Наследник: Квадрат (❌ ошибка дизайна) ```python class Square(Rectangle): def set_width(self, w): self._width = w self._height = w def set_height(self, h): self._width = h self._height = h ``` --- ## Где возникает проблема Код, который **корректно работает с Rectangle**: ```python def resize(rect: Rectangle): rect.set_width(5) rect.set_height(10) return rect.area() ``` ### Ожидание: ```text 5 × 10 = 50 ``` ### Реальность: ```python resize(Rectangle(2, 3)) # 50 ✅ resize(Square(2, 2)) # 100 ❌ ``` 👉 Подстановка `Square` **меняет поведение** 👉 Контракт `Rectangle` **нарушен** --- ## Почему это нарушение LSP **Принцип Лисков (LSP):** > Объекты дочернего класса должны быть взаимозаменяемы с объектами базового класса **без изменения корректности программы** В нашем случае: * `Rectangle` допускает **независимое изменение ширины и высоты** * `Square` **запрещает это** ❌ Значит `Square` **НЕ является корректным подтипом** `Rectangle` в ООП-смысле --- ## Ключевая мысль (очень важная) > **Математическое “is-a” ≠ ООП “is-a”** * В геометрии: квадрат ⊂ прямоугольник ✅ * В ООП: квадрат **не подходит** под контракт прямоугольника ❌ --- ## Правильные решения ### ✅ Вариант 1: Не использовать наследование ```python class Shape: def area(self): pass class Rectangle(Shape): ... class Square(Shape): ... ``` ✔ Нет нарушения ✔ Явные контракты ✔ Без сюрпризов --- ### ✅ Вариант 2: Сделать объект неизменяемым ```python class Rectangle: def __init__(self, width, height): self.width = width self.height = height ``` * Нет `set_width` / `set_height` * Нет нарушения контракта --- ### ❌ Вариант 3: «Костыли» (плохо) ```python def set_width(self, w): if isinstance(self, Square): raise Exception() ``` ❌ Ломает полиморфизм ❌ Антипаттерн --- ## Почему этот пример так любят на собеседованиях Он проверяет, понимаешь ли ты: * разницу между **наследованием и моделированием** * что такое **контракт класса** * что **ООП — это не про иерархии, а про поведение** * принцип **composition over inheritance** --- ## Короткая шпаргалка * Проблема прямоугольника = нарушение **LSP** * Наследник **не должен усиливать ограничения** * Если поведение меняется → это **не is-a** * Сомневаешься → **композиция лучше наследования** --- Если хочешь, дальше могу: * разобрать **аналогичные ловушки (Bird → Penguin)** * связать пример с **SOLID на реальных сервисах** * показать, как **Python duck typing** частично обходит проблему * разобрать **как объяснять это на собеседовании за 30 секунд** Скажи, что полезнее 👍