Что это такое
Паттерн Декоратор (Decorator) добавляет объекту поведение, оборачивая его в другой объект с тем же интерфейсом. Каждая обёртка выполняет свою дополнительную часть работы и передаёт вызов дальше — тому объекту, который она оборачивает.
Поскольку у обёртки и оборачиваемого объекта общий интерфейс, обёртки можно накладывать друг на друга в любых сочетаниях — как слои одежды.
Синтаксис @decorator в Python — это та же идея, применённая к функциям: обёртка вокруг вызова. На этой странице показана объектная форма, где слои выбираются и накладываются во время выполнения программы.
Пример: оборачиваем отправителя уведомлений
Простой отправитель уведомлений отправляет письмо по email. Одна обёртка добавляет запись в лог перед отправкой, другая — SMS после неё. Вызывающий код по-прежнему видит обычный Notifier.
Слои вокруг ядра
WithLog(WithSMS(EmailNotifier())) читается изнутри наружу: в ядре — email, вокруг него — SMS, снаружи всего — логирование. Любой слой можно добавить, убрать или переставить, не трогая остальные.
Вывод: полная программа на Python
from typing import Protocol
class Notifier(Protocol):
def send(self, msg: str) -> None: ...
class EmailNotifier:
def send(self, msg: str) -> None:
print("email:", msg)
# Каждый декоратор оборачивает Notifier и добавляет один слой.
class WithLog:
def __init__(self, wrapped: Notifier):
self.wrapped = wrapped
def send(self, msg: str) -> None:
print("лог: отправка уведомления")
self.wrapped.send(msg)
class WithSMS:
def __init__(self, wrapped: Notifier):
self.wrapped = wrapped
def send(self, msg: str) -> None:
self.wrapped.send(msg)
print("sms:", msg)
n = WithLog(WithSMS(EmailNotifier()))
n.send("сервер не работает")
Когда его использовать
Используйте его, чтобы добавить необязательное поведение, которое можно комбинировать, — логирование, кэширование, повторные попытки, метрики — вокруг ядра, которое вы не хотите изменять. Middleware (промежуточное ПО) в веб-серверах — это именно этот паттерн.
Не используйте его, когда поведение не является необязательным и его не нужно комбинировать, — тогда ему место внутри самого класса, где его легче найти.
Плюсы и минусы
Плюсы: поведение добавляется без изменения основного класса; слои свободно комбинируются; каждый слой небольшой, и его можно тестировать отдельно.
Минусы: глубокую стопку обёрток трудно отлаживать — вызов проходит долгий путь, прежде чем что-то сделать; строки создания объектов становятся длинными.