Decorator
Паттерн, который наращивает поведение объекта слоями-обёртками — без взрыва числа подклассов.
Decorator динамически добавляет объекту новые обязанности, оборачивая его в объект с тем же интерфейсом.
Какую задачу решает
Нужно добавить к объекту возможности в разных комбинациях: кофе с молоком, кофе с молоком и сиропом, кофе с сиропом без молока. Через наследование пришлось бы плодить классы под каждую комбинацию. Decorator вместо этого оборачивает объект слоями, каждый слой добавляет своё и делегирует остальное вложенному объекту.
Идея и реализация
Ключ: декоратор реализует тот же интерфейс, что и оборачиваемый объект, хранит ссылку на него и в своих методах вызывает его, добавляя поведение.
from abc import ABC, abstractmethod
class Coffee(ABC):
@abstractmethod
def cost(self): ...
@abstractmethod
def desc(self): ...
class Espresso(Coffee):
def cost(self): return 100
def desc(self): return "эспрессо"
class Addon(Coffee): # базовый декоратор
def __init__(self, wrapped: Coffee):
self._wrapped = wrapped
class Milk(Addon):
def cost(self): return self._wrapped.cost() + 30
def desc(self): return self._wrapped.desc() + " + молоко"
class Syrup(Addon):
def cost(self): return self._wrapped.cost() + 20
def desc(self): return self._wrapped.desc() + " + сироп"
order = Syrup(Milk(Espresso())) # оборачиваем слоями
print(order.desc(), "=", order.cost())
Вывод:
эспрессо + молоко + сироп = 150
Каждый слой — самостоятельный объект. Syrup(Milk(Espresso())) читается как «эспрессо, потом молоко, потом сироп». Комбинации собираются в рантайме, новые добавки — это новые классы-декораторы, а не комбинаторный взрыв подклассов.
Чем отличается от наследования
| Наследование | Decorator |
| Фиксируется на этапе компиляции | Собирается в рантайме |
| N добавок → до 2^N классов | N добавок → N классов |
| Одна цепочка наследования | Любые комбинации обёрток |
Decorator против @декораторов Python
Не путайте паттерн с синтаксисом @ в Python. Питоновские декораторы функций — родственная идея (обёртка), но это другой механизм. Паттерн Decorator из GoF — это про объекты и интерфейсы, как в примере выше.
Где встречается
Потоки ввода-вывода (буферизация поверх файла поверх сжатия), middleware в веб-фреймворках, обёртки прав доступа и логирования, UI-компоненты со скроллом/рамкой.
Итог
- Decorator добавляет поведение, оборачивая объект в объект того же интерфейса.
- Избавляет от комбинаторного взрыва подклассов.
- Комбинации задаются в рантайме — гибче наследования.