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

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

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

Содержание

  • Область применения
  • Реализация
  • Итоги

Область применения

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

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

Реализация

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

Удобно реализовать шаблон через абстрактные классы, это позволит в дальнейшем легко модифицировать приложение на базе абстракций. Смысл предыдущего утверждения будет понятен после пояснений кода. Итак, приступим.

//родитель компонента приложения, определяющий набор его базовых функций
abstract class AbstractComponent
{
    abstract public function operation();
    ...
}

//здесь реализуется набор базовых функций компонента приложения
class Component1 extends AbstractComponent
{
    //определяем задекларированный в абстрактном классе метод
    public function operation() 
    {
        // ... реализация
    }
    
    // ... реализация других базовых функций компонента
}

//абстрактный класс, определяющий основной набор новых функций
abstract class AbstractDecorator extends AbstractComponent
{
    protected $component;
   
    public function __construct(AbstractComponent $component) 
    {
        $this->component = $component;
    }
    
    // ... реализация других основных новых функций компонента при необходимости
}

// набор1 новых функций компонента
class ComponentDecorator1 extends AbstractDecorator
{
    public function operation()
    {
        // ... расширенная функциональность, выполняемая до основных
        $this->component->operation();
        // ... расширенная функциональность, выполняемая после основных
    }
}

// набор2 новых функций компонента
class ComponentDecorator2 extends AbstractDecorator
{
    public function operation()
    {
        // ... расширенная функциональность, выполняемая до основных
        $this->component->operation();
        // ... расширенная функциональность, выполняемая после основных
    }
}

Итоги

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

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