Паттерн Декоратор в PHP
Короткий скринкаст: паттерн «Декоратор» (Decorator) — оборачиваем объект и добавляем поведение на лету. Цепочка уведомлений: email → sms → slack.
Описание
Декоратор позволяет динамически добавлять объекту новые обязанности, не меняя его исходного класса. Мы создаём общий интерфейс NotifierInterface, базовый класс EmailNotifier и абстрактный декоратор NotifierDecorator, который хранит обёрнутый объект. Конкретные декораторы SmsDecorator и SlackDecorator расширяют поведение, оборачивая друг друга — так получается цепочка вызовов.
UML-диаграмма
Код
NotifierInterface.php
interface NotifierInterface
{
public function send(string $message): string;
}
EmailNotifier.php
class EmailNotifier implements NotifierInterface
{
public function send(string $message): string
{
return "email: $message";
}
}
NotifierDecorator.php
class NotifierDecorator implements NotifierInterface
{
protected NotifierInterface $wrapped;
public function __construct(NotifierInterface $wrapped)
{
$this->wrapped = $wrapped;
}
public function send(string $message): string
{
return $this->wrapped->send($message);
}
}
SmsDecorator.php
class SmsDecorator extends NotifierDecorator
{
public function send(string $message): string
{
return $this->wrapped->send($message).' | sms: '.$message;
}
}
SlackDecorator.php
class SlackDecorator extends NotifierDecorator
{
public function send(string $message): string
{
return $this->wrapped->send($message).' | slack: '.$message;
}
}
demo.php
require_once 'NotifierInterface.php';
require_once 'EmailNotifier.php';
require_once 'NotifierDecorator.php';
require_once 'SmsDecorator.php';
require_once 'SlackDecorator.php';
$notifier = new SlackDecorator(
new SmsDecorator(
new EmailNotifier()
)
);
echo $notifier->send('order ready').PHP_EOL;
Пример результата
После php demo.php в терминале появится цепочка уведомлений:
email: order ready | sms: order ready | slack: order ready
Преимущества
- Добавляет поведение без подклассов и правок исходного класса.
- Декораторы свободно комбинируются и наслаиваются в любом порядке.
- Поведение можно включать и выключать во время выполнения.
- Каждый декоратор отвечает за одну задачу.
Когда использовать
- Когда нужно динамически добавлять объекту обязанности.
- Когда наследование привело бы к взрыву подклассов.
- Когда поведения независимы и комбинируются.
- Когда нельзя менять исходный класс (чужой или финальный).
Использование в Laravel
В Laravel Декоратор — это кэш-репозиторий: Illuminate\Cache\Repository оборачивает стор (Illuminate\Contracts\Cache\Store) и добавляет поверх него remember(), теги и события, не меняя сам драйвер. Так же устроены каналы логов в Illuminate\Log\LogManager и обёртки вокруг HTTP-клиента: поведение навешивается слоями.