НЕ МОЛЧИ!!!    Сделай что-нибудь,
чтобы остановить войну России в Украине.
...бойтесь людей равнодушных - именно с их молчаливого согласия происходят все самые ужасные преступления на свете.   ("Репортаж с петлёй на шее")

Шаблон проектирования Decorator (Декоратор)

Описывается структурный шаблон проектирования Decorator, приведены описание и пример реализации на PHP.

Содержание

  • Применение
  • Структура шаблона
  • Реализация
  • Итоги

Применение

Шаблон проектирования Decorator применяют в случае, когда предстоит достаточно часто изменять функциональность какого-то компонента приложения. Обычно для расширения функций объекта практикуется создание наследников. Но при большом количестве изменений иерархия очень быстро «растолстеет». Также нередки случаи, когда программист не имеет возможности создавать наследников. В этом случае вам поможет Decorator. Шаблон часто используют в случаях:

  • если к компоненту надо добавлять достаточно много небольших функций;
  • если требуются однотипные компоненты с небольшими различиями в поведении;
  • когда требуется изменить поведение отдельного экземпляра класса не влияя на поведение остальных экземпляров.

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

Структура шаблона

Итак, при реализации Декоратора, как и в случае с шаблоном Адаптер, вокруг исходного компонента создается обёртка, которая добавляет функционал. Новые функции можно добавлять как до выполнения исходного кода, так и после. Также важно отметить, что интерфейс декоратора совпадает с интерфейсом изменяемого компонента плюс новые функции.

Итак, в реализации шаблона Decorator участвуют такие компоненты.

  • IComponent — интерфейс, определяющий набор базовых функций компонента.
  • Component — класс, реализующий набор базовых функций, определенный в IComponent.
  • AbstractDecorator — абстрактный класс декораторов, определяющий обязательный функционал декораторов-наследников.
  • ComponentDecorator1 — класс конкретного декоратора, дополняющий поведение компонента.

Реализация

Теперь приступим к реализации. Что будет в примере?

  • Определим интерфейс компонента .
  • Создадим класс компонента, реализующий интерфейс.
  • Создадим абстрактный класс декоратора, определяющий базовое поведение декораторов.
  • Создадим класс конкретного наследника, расширяющий базовый функционал декоратора.

Создадим интерфейс, который будут исполнять компонент и декоратор, для простоты в нем лишь один метод.

declare(strict_types=1);

interface IComponent
{
    public function operation();
}

Теперь создадим класс компонента, который реализует интерфейс абстрактного AbstractComponent.


class Component implements IComponent
{
    public function operation(): void
    {
        // Основная функциональность
    }
}

Создаём абстрактный класс декоратора, на основе которого будут создаваться конкретные декораторы компонента.


abstract class AbstractDecorator implements IComponent
{
    protected IComponent $component;

    public function __construct(IComponent $component)
    {
        $this->component = $component;
    }

    public function operation(): void
    {
        $this->component->operation();
    }
}

Обратите внимание, что при создании объект декоратора получает ссылку на изменяемый компонент. Это позволяет ему сохранить функциональность исходного компонента.

Теперь можно создать класс конкретного декоратора, который реализует функции родителя и добавляет новую функциональность.


class ComponentDecorator1 extends AbstractDecorator
{
    public function operation(): void
    {
        // Расширенная функциональность, выполняемая до основной
        // ...

        $this->component->operation();

        // Расширенная функциональность, выполняемая после основной
        // ...
    }
}

UML диаграмма классов примера выглядит следующим образом.

Decoration pattern uml diagram

Итоги

Надеюсь, все достаточно понятно из комментариев. Хочется только отметить следующее. Структурный шаблон Decorator является хорошей альтернативой наследованию, упрощающей код приложения. 
В классе AbstractDecorator задается одна и больше новых функций изменяемого компонента. Кроме того, при создании экземпляра декоратора ему передается ссылка на исходный компонент, это позволяет его использование без изменений.
В классе  ComponentDecorator1 реализуется новое поведение изменяемого компонента. При создании ему передается ссылка на изменяемый объект, она может ссылаться как на исходный компонент, так и предыдущий декоратор, это позволяет строить произвольную цепочку изменений.
К достоинствам Декоратора относится:

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

Из недостатков можно перечислить следующие:

  • необходимость создания множества мелких классов при большом количестве изменений;
  • громоздкая инициализация в случае длинной цепочки декораторов;
  • обработка может зависеть от порядка вызовов декораторов.

Перейти к списку шаблонов проектирования.