PHP Decorator Design Pattern Структурный паттерн

Паттерн Декоратор в PHP

Короткий скринкаст: паттерн «Декоратор» (Decorator) — оборачиваем объект и добавляем поведение на лету. Цепочка уведомлений: email → sms → slack.

Decorator Pattern — открыть страницу урока

Описание

Декоратор позволяет динамически добавлять объекту новые обязанности, не меняя его исходного класса. Мы создаём общий интерфейс NotifierInterface, базовый класс EmailNotifier и абстрактный декоратор NotifierDecorator, который хранит обёрнутый объект. Конкретные декораторы SmsDecorator и SlackDecorator расширяют поведение, оборачивая друг друга — так получается цепочка вызовов.

UML-диаграмма

classDiagram class NotifierInterface { <<interface>> +send(message: string): string } class EmailNotifier { +send(message: string): string } class NotifierDecorator { #wrapped: NotifierInterface +__construct(wrapped: NotifierInterface) +send(message: string): string } class SmsDecorator { +send(message: string): string } class SlackDecorator { +send(message: string): string } NotifierInterface <|-- EmailNotifier : implements NotifierInterface <|-- NotifierDecorator : implements NotifierDecorator <|-- SmsDecorator : extends NotifierDecorator <|-- SlackDecorator : extends NotifierDecorator o-- NotifierInterface : wrapped

Код

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-клиента: поведение навешивается слоями.