Структурные паттерны: Декоратор (Decorator) C#
Назначение:
Динамически добавляет объекту новые обязанности. Является гибкой альтернативой порождению подклассов с целью расширения функциональности.
Когда следует использовать декораторы?
Когда надо динамически добавлять к объекту новые функциональные возможности. При этом данные возможности могут быть сняты с объекта
Когда применение наследования неприемлемо. Например, если нам надо определить множество различных функциональностей и для каждой функциональности наследовать отдельный класс, то структура классов может очень сильно разрастись. Еще больше она может разрастись, если нам необходимо создать классы, реализующие все возможные сочетания добавляемых функциональностей.
Схема примера использования паттерна декоратор из реальной жизни:
UML схема паттерна Decorator:
Участники
-
Component: абстрактный класс, который определяет интерфейс для наследуемых объектов.
-
ConcreteComponent: конкретная реализация компонента, в которую с помощью декоратора добавляется новая функциональность.
-
Decorator: собственно декоратор, реализуется в виде абстрактного класса и имеет тот же базовый класс, что и декорируемые объекты. Поэтому базовый класс Component должен быть по возможности легким и определять только базовый интерфейс.
Класс декоратора также хранит ссылку на декорируемый объект в виде объекта базового класса Component и реализует связь с базовым классом как через наследование, так и через отношение агрегации.
-
Классы ConcreteDecoratorA и ConcreteDecoratorB представляют дополнительные функциональности, которыми должен быть расширен объект ConcreteComponent.
Реализация шаблона декоратор на C#
abstract class Component
{
public abstract void Operation();
}
class ConcreteComponent : Component
{
public override void Operation()
{}
}
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
component.Operation();
}
}
class ConcreteDecoratorA : Decorator
{
public override void Operation()
{
base.Operation();
}
}
class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
}
}
Недостатки декораторов
Декоратор, как и большинство других паттернов, имеет некоторые недостатки
- Чувствительность к порядку. Код инициализации декораторов очень важен, поскольку именно в процессе создания определяются вложенность и порядок исполнения разных декораторов.
- Сложность отладки. Разработчику, незнакомому с этим паттерном, замер времени исполнения или кэширование результатов декораторами может показаться черной магией. Отлаживать проблемы, которые возникли внутри декоратора, может быть довольно сложно.
- Увеличение сложности. Декоратор является довольно тяжеловесным паттерном, к которому стоит прибегать тогда, когда выделяемый аспект поведения достаточно сложен. Если нужно кэшировать результаты в одном из десяти методов, то сложность, привнесенная декоратором, будет неоправданна.
Применимость
Декоратор позволяет динамически расширять поведение объектов. Он идеально подходит для расширения поведения всех методов интерфейса, которое не является частью основной функциональности. Если кэшировать нужно лишь результаты одного метода класса, то использование декоратора будет слишком тяжеловесным.
Декораторы применяются для добавления всем методам интерфейса некоторого поведения, которое не является частью основной функциональности. Декораторы отлично подходят для решения следующих задач:
- кэширования результатов работы;
- замера времени исполнения методов;
- логирования аргументов;
- управления доступом пользователей;
- модификации аргументов или результата работы методов упаковки/распаковки, шифрования и т. п.