Описывается структурный шаблон проектирования 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 диаграмма классов примера выглядит следующим образом.
Итоги
Надеюсь, все достаточно понятно из комментариев. Хочется только отметить следующее. Структурный шаблон Decorator является хорошей альтернативой наследованию, упрощающей код приложения.
В классе AbstractDecorator задается одна и больше новых функций изменяемого компонента. Кроме того, при создании экземпляра декоратора ему передается ссылка на исходный компонент, это позволяет его использование без изменений.
В классе ComponentDecorator1 реализуется новое поведение изменяемого компонента. При создании ему передается ссылка на изменяемый объект, она может ссылаться как на исходный компонент, так и предыдущий декоратор, это позволяет строить произвольную цепочку изменений.
К достоинствам Декоратора относится:
- возможность динамически изменять обязанности базового объекта;
- соответствие принципу единой ответственности, т.к. новая операция не добавляется в исходный класс, а выносится в отдельный;
- позволяет заменить чрезмерную иерархию классов в случае наследования на достаточно компактные обертки;
- при использовании нескольких оберток исходный компонент будет иметь их суммарную обработку;
- позволяет комбинировать новые обязанности с помощью различных обёрток.
Из недостатков можно перечислить следующие:
- необходимость создания множества мелких классов при большом количестве изменений;
- громоздкая инициализация в случае длинной цепочки декораторов;
- обработка может зависеть от порядка вызовов декораторов.
Перейти к списку шаблонов проектирования.
