06.01 2011

Decorator

Перед прочтением ознакомьтесь с введением в паттерны проектирования на PHP, в котором описаны принятые соглашения и понятия. Данная статья дополняется с некоторой периодичностью, так что если вы ее читали ранее, не факт что данные не изменились.

Decorator (Декоратор) относиться к классу структурных паттернов. Он используется для динамического расширения функциональности объекта. Является гибкой альтернативой наследованию.

Сущность работы паттерна декоратор заключается в "оборачивании" готового объекта новым функционалом, при этом весь оригинальный интерфейс объекта остается доступным (декоратор переадресует все запросы объекту). Смысл заключается в том, чтобы можно было безболезненно комбинировать различные декораторы в произвольном порядке, навешивая их на различные объекты. В некотором роде, это похоже на технологию traits, за исключением того, что декораторы динамически навешиваются на объект, а traits статически на класс.

Decorator

Структурно паттерн представляет собой такую схему:

Decorator UML

Как видно из схемы, и конкретный объект и декоратор имеют общий родительский класс, это сделано для того, чтобы публичный интерфейс у этих объектов совпадал. В классе decorator все методы перегружаются, и заменяются на делегирование компоненту. В конкретных реализациях декоратора, необходимые методы дополняются функционалом. Тем самым у нас получается наследование без наследования. При такой структуре нам не важно является ли компонент декоратором или конкретной реализацией, потому как интерфейс у них совпадает, и, как результат, мы можем делать цепочки декораторов.

На PHP данный паттерн можно представить следующим образом:

abstract class Component
{
    abstract public function Operation();
}

class ConcreteComponent extends Component
{
    public function Operation()
    {
        return 'I am component';
    }
}

abstract class Decorator extends Component
{
    protected
        $_component = null;

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

    protected function getComponent()
    {
        return $this -> _component;
    }

    public function Operation()
    {
        return $this -> getComponent() -> Operation();
    }
}

class ConcreteDecoratorA extends Decorator
{
    public function Operation()
    {
        return '<a>' . parent::Operation() . '</a>';
    }
}

class ConcreteDecoratorB extends Decorator
{
    public function Operation()
    {
        return '<strong>' . parent::Operation() . '</strong>';
    }
}

// Example

$Element = new ConcreteComponent();
$ExtendedElement = new ConcreteDecoratorA($Element);
$SuperExtendedElement = new ConcreteDecoratorB($ExtendedElement);

print $SuperExtendedElement -> Operation(); // <strong><a>I am component</a></strong>

Исходный код скачать можно тут

В некоторых случаях, вместо переопределения всех методов используется магический метод __call

Результат

  • Большая гибкость
  • Легкие классы на верхних уровнях абстракции
  • Множество мелких объектов

В некоторых случаях, декоратор и конкретный компонент не наследуют один и тот же класс. В нашем примере к слову, это было излишним. Полное соответствие интерфейса также не всегда требуется (в таком случае декоратор выступает в роли адаптера).

comments powered by Disqus