Перейти к основному содержимому

Что это такое

Паттерн Декоратор (Decorator) добавляет объекту поведение, оборачивая его в другой объект с тем же интерфейсом. Каждая обёртка выполняет свою дополнительную часть работы и передаёт вызов дальше — тому объекту, который она оборачивает.

Поскольку у обёртки и оборачиваемого объекта общий интерфейс, обёртки можно накладывать друг на друга в любых сочетаниях — как слои одежды.

Синтаксис @decorator в Python — это та же идея, применённая к функциям: обёртка вокруг вызова. На этой странице показана объектная форма, где слои выбираются и накладываются во время выполнения программы.


Пример: оборачиваем отправителя уведомлений

Простой отправитель уведомлений отправляет письмо по email. Одна обёртка добавляет запись в лог перед отправкой, другая — SMS после неё. Вызывающий код по-прежнему видит обычный Notifier.

Слои вокруг ядра

WithLog(WithSMS(EmailNotifier())) читается изнутри наружу: в ядре — email, вокруг него — SMS, снаружи всего — логирование. Любой слой можно добавить, убрать или переставить, не трогая остальные.

Prompt
Напишите программу на Python с паттерном Декоратор: протокол Notifier (метод send), простой класс EmailNotifier, класс-обёртку WithLog, который печатает строку лога перед тем, как передать вызов дальше, и класс-обёртку WithSMS, который после этого ещё и отправляет SMS. Наложите обёртки вокруг отправителя email и отправьте одно сообщение.
Вывод: полная программа на Python
Layers around a core

Когда его использовать

Используйте его, чтобы добавить необязательное поведение, которое можно комбинировать, — логирование, кэширование, повторные попытки, метрики — вокруг ядра, которое вы не хотите изменять. Middleware (промежуточное ПО) в веб-серверах — это именно этот паттерн.

Не используйте его, когда поведение не является необязательным и его не нужно комбинировать, — тогда ему место внутри самого класса, где его легче найти.


Плюсы и минусы

Плюсы: поведение добавляется без изменения основного класса; слои свободно комбинируются; каждый слой небольшой, и его можно тестировать отдельно.

Минусы: глубокую стопку обёрток трудно отлаживать — вызов проходит долгий путь, прежде чем что-то сделать; строки создания объектов становятся длинными.