Что это такое
Паттерн Декоратор добавляет объекту поведение, оборачивая его в другой объект с тем же интерфейсом. Каждая обёртка делает свою добавочную часть работы и передаёт вызов дальше — тому, что она оборачивает.
Поскольку обёртка и обёрнутый объект имеют общий интерфейс, обёртки складываются в любых комбинациях — как слои одежды.
Пример: оборачиваем нотификатор
Простой нотификатор отправляет письмо. Одна обёртка добавляет запись в журнал перед отправкой, другая — отправку SMS после неё. Вызывающий код по-прежнему видит обычный Notifier.
Слои вокруг ядра
Запись new WithLog(new WithSMS(new EmailNotifier())) читается изнутри наружу: письмо в ядре, SMS вокруг него, журналирование — самый внешний слой. Любой слой можно добавить, убрать или переставить, не затрагивая остальные.
Вывод: PHP-скрипт
<?php
interface Notifier {
public function send(string $msg): void;
}
class EmailNotifier implements Notifier {
public function send(string $msg): void {
echo "email: ", $msg, "\n";
}
}
// Каждый декоратор оборачивает Notifier и добавляет один слой.
class WithLog implements Notifier {
public function __construct(private Notifier $wrapped) {}
public function send(string $msg): void {
echo "log: отправка уведомления\n";
$this->wrapped->send($msg);
}
}
class WithSMS implements Notifier {
public function __construct(private Notifier $wrapped) {}
public function send(string $msg): void {
$this->wrapped->send($msg);
echo "sms: ", $msg, "\n";
}
}
$n = new WithLog(new WithSMS(new EmailNotifier()));
$n->send("server is down");
Когда его применять
Применяйте его, чтобы добавлять необязательное и комбинируемое поведение — журналирование, кеширование, повторные попытки, метрики — вокруг ядра, которое вы не хотите изменять. HTTP-посредники (middleware) в PHP-приложениях оборачивают обработчики запросов именно так.
Откажитесь от него, когда поведение не является необязательным и не комбинируется: тогда его место внутри самого класса, где его легче найти.
Плюсы и минусы
Плюсы: поведение добавляется без изменения основного класса; слои свободно комбинируются; каждый слой мал и тестируется отдельно.
Минусы: глубокий стек обёрток трудно отлаживать — вызов проходит длинный путь, прежде чем что-то сделать; строки создания объектов становятся длинными.