- Декоратор (шаблон проектирования)
-
Шаблон проектирования Декоратор Decorator Тип: структурный
Назначение: для динамического подключения к объекту дополнительных обязательств
Плюсы: - нет необходимости создавать подклассы для расширения функциональности объекта;
- возможность динамически подключать новую функциональность до или после основной функциональности объекта
ConcreteComponent
.
Родственные шаблоны: Описан в Design Patterns Да
Декоратор, Decorator — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту. Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
Известен также под менее распространённым названием Обёртка (Wrapper)
, которое во многом раскрывает суть реализации шаблона.Основные характеристики
Задача
Объект, который предполагается использовать, выполняет основные функции. Однако может потребоваться добавить к нему некоторую дополнительную функциональность, которая будет выполняться до, после или даже вместо основной функциональности объекта.
Способ решения
Декоратор предусматривает расширение функциональности объекта без определения подклассов.
Участники
Класс
ConcreteComponent
— класс, в который с помощью шаблона Декоратор добавляется новая функциональность. В некоторых случаях базовая функциональность предоставляется классами, производными от классаConcreteComponent
. В подобных случаях классConcreteComponent
является уже не конкретным, а абстрактным. Абстрактный классComponent
определяет интерфейс для использования всех этих классов.Следствия
1. Добавляемая функциональность реализуется в небольших объектах. Преимущество состоит в возможности динамически добавлять эту функциональность до или после основной функциональности объекта
ConcreteComponent
.
2. Позволяет избегать перегрузки функциональными классами на верхних уровнях иерархии
3. Декоратор и его компоненты не являются идентичнымиРеализация
Создается абстрактный класс, представляющий как исходный класс, так и новые, добавляемые в класс функции. В классах-декораторах новые функции вызываются в требуемой последовательности — до или после вызова последующего объекта.
При желании остаётся возможность использовать исходный класс (без расширения функциональности), если на его объект сохранилась ссылка.
Замечания и комментарии
- Хотя объект-декоратор может добавлять свою функциональность до или после функциональности основного объекта, цепочка создаваемых объектов всегда должна заканчиваться объектом класса
ConcreteComponent
. - Базовые классы языка Java широко используют шаблон Декоратор для организации обработки операций ввода-вывода.
- И декоратор, и адаптер являются обертками вокруг объекта — хранят в себе ссылку на оборачиваемый объект и часто передают в него вызовы методов. Отличие декоратора от адаптера в том, что адаптер имеет внешний интерфейс, отличный от интерфейса оборачиваемого объекта, и используется именно для стыковки разных интерфейсов. Декоратор же имеет точно такой же интерфейс, и используется для добавления функциональности.
- Для расширения функциональности класса возможно использовать как декораторы, так и стратегии. Декораторы оборачивают объект снаружи, стратегии же вставляются в него внутрь по неким интерфейсам. Недостаток стратегии: класс должен быть спроектирован с возможностью вставления стратегий, декоратор же не требует такой поддержки. Недостаток декоратора: он оборачивает ровно тот же интерфейс, что предназначен для внешнего мира, что вызывает смешение публичного интерфейса и интерфейса кастомизации, которое не всегда желательно.
Применение шаблона
Драйверы-фильтры в ядре Windows (архитектура WDM) представляют собой декораторы. Несмотря на то, что WDM реализована на не-объектном языке Си, в ней четко прослеживаются паттерны проектирования — декоратор, цепочка ответственности, и команда (объект IRP).
Архитектура COM не поддерживает наследование реализаций, вместо него предлагается использовать декораторы (в данной архитектуре это называется «агрегация»). При этом архитектура решает (с помощью механизма pUnkOuter) проблему object identity, возникающую при использовании декораторов — identity агрегата есть identity его самого внешнего декоратора.
Примеры
Пример реализации шаблона на Java
Пример реализацииpublic interface InterfaceComponent { void doOperation(); } abstract class Decorator implements InterfaceComponent{ protected InterfaceComponent component; public Decorator (InterfaceComponent c){ component = c; } public void doOperation(){ component.doOperation(); } public void newOperation(){ System.out.println("Do Nothing"); } } class MainComponent implements InterfaceComponent{ @Override public void doOperation() { System.out.print("World!"); } } class DecoratorSpace extends Decorator{ public DecoratorSpace(InterfaceComponent c){ super(c); } @Override public void doOperation() { System.out.print(" "); super.doOperation(); } @Override public void newOperation(){ System.out.println("New space operation"); } } class DecoratorComma extends Decorator{ public DecoratorComma(InterfaceComponent c){ super(c); } @Override public void doOperation() { System.out.print(","); super.doOperation(); } @Override public void newOperation(){ System.out.println("New comma operation"); } } class DecoratorHello extends Decorator{ public DecoratorHello(InterfaceComponent c){ super(c); } @Override public void doOperation() { System.out.print("Hello"); super.doOperation(); } @Override public void newOperation(){ System.out.println("New hello operation"); } } class Main { public static void main (String... s){ Decorator c = new DecoratorHello(new DecoratorComma(new DecoratorSpace(new MainComponent()))); c.doOperation(); // Результат выполнения программы "Hello, World!" c.newOperation(); // New hello operation } }
Пример реализации шаблона на C#
Пример реализацииusing System; namespace Decorator { class MainApp { static void Main() { // Create ConcreteComponent and two Decorators ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); // Link decorators d1.SetComponent(c); d2.SetComponent(d1); d2.Operation(); // Wait for user Console.Read(); } } abstract class Component { public abstract void Operation(); } class ConcreteComponent : Component { public override void Operation() { Console.WriteLine("ConcreteComponent.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 { private string addedState; public override void Operation() { base.Operation(); addedState = "New State"; Console.WriteLine("ConcreteDecoratorA.Operation()"); } } // "ConcreteDecoratorB" class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("ConcreteDecoratorB.Operation()"); } void AddedBehavior() { } } }
Пример реализации на C++
Пример реализации#include <iostream> #include <memory> class IComponent { public: virtual void operation() = 0; virtual ~IComponent(){} }; class Component : public IComponent { public: virtual void operation() { std::cout<<"World!"<<std::endl; } }; class DecoratorOne : public IComponent { std::auto_ptr<IComponent> m_component; public: DecoratorOne(IComponent * component): m_component(component) {} virtual void operation() { std::cout << ", "; m_component->operation(); } }; class DecoratorTwo : public IComponent { std::auto_ptr<IComponent> m_component; public: DecoratorTwo(IComponent * component): m_component(component) {} virtual void operation() { std::cout << "Hello"; m_component->operation(); } }; int main() { DecoratorTwo obj(new DecoratorOne(new Component())); obj.operation(); // prints "Hello, World!\n" return 0; }
Пример на D-language
Исходный текст на языке Dimport std.stdio; abstract class TFigure { protected: string name; public: string GetInfo(); } static class TEmpty: TFigure { public: string GetInfo() { return ""; }; } class TCircle: TFigure { protected: TFigure figure; public: this(TFigure theFigure) { figure = theFigure; name = " circle "; } string GetInfo() { return (name ~ figure.GetInfo()); } } class TBar: TFigure { protected: TFigure figure; public: this(TFigure theFigure) { figure = theFigure; name = " bar "; } string GetInfo() { return (figure.GetInfo() ~ name); } } void main() { TFigure figures = new TBar(new TCircle(new TBar(new TCircle(new TEmpty())))); writeln(figures.GetInfo()); }
Пример реализации на Python
Пример реализацииfrom abc import ABCMeta, abstractmethod class IOperator(object): """ Интерфейс, который должен реализовать как декоратор, так и оборачиваемый объект. """ __metaclass__ = ABCMeta @abstractmethod def operator(self): pass class Component(IOperator): """Компонент программы""" def operator(self): return 10.0 class Wrapper(IOperator): """Декоратор""" def __init__(self, obj): self.obj = obj def operator(self): return self.obj.operator() + 5.0 comp = Component() comp = Wrapper(comp) print comp.operator() # 15.0
Пример реализации на PHP
Пример реализацииabstract class AbstractComponent { abstract public function operation(); } class ConcreteComponent extends AbstractComponent { public function operation() { // ... } } abstract class AbstractDecorator extends AbstractComponent { private $_component; public function __construct(AbstractComponent $component) { $this->_component = $component; } public function operation() { $this->_component->operation(); } } class ConcreteDecorator extends AbstractDecorator { public function operation() { // ... расширенная функциональность ... parent::operation(); // ... расширенная функциональность ... } } $decoratedComponent = new ConcreteDecorator( new ConcreteComponent() ); $decoratedComponent->operation();
Пример реализации на CoffeeScript
Пример реализации# Компонент class Notebook # Маркетинг price : 500 # $ # Характеристики hdd : 320 # GB ram : 4 # GB core : 'i5 2.3' # GHz # Декоратор class NovaNotebook constructor : (product) -> @price = product.price * 1.3 # Декоратор class ImportNotebook constructor : (product) -> @price = product.price * 1.5 # Декоратор class AppleNotebook constructor : (product) -> @price = product.price * 2.1 macBookInRussia = new ImportNotebook new NovaNotebook new AppleNotebook new Notebook console.log(macBookInRussia.price)
Примеры реализации на JavaScript
Пример реализацииШаблон декоратор в языках с динамической типизацией может быть применен без интерфейсов и традиционного для ООП наследования.
Этот пример скопирован с английской версии статьи. Расчет стоимости кофе:
// ConcreteComponent (класс для последующего декорирования) function Coffee() { this.cost = function() { return 1; }; } // Decorator A function Milk(coffee) { this.cost = function() { return coffee.cost() + 0.5; }; } // Decorator B function Whip(coffee) { this.cost = function() { return coffee.cost() + 0.7; }; } // Decorator C function Sprinkles(coffee) { this.cost = function() { return coffee.cost() + 0.2; }; } // Можно использовать, например, так: var coffee = new Milk(new Whip(new Sprinkles(new Coffee()))); alert( coffee.cost() ); // Или более наглядно: var coffee = new Coffee(); coffee = new Sprinkles(coffee); coffee = new Whip(coffee); coffee = new Milk(coffee); alert(coffee.cost());
Реализация имеющегося выше C# примера. В ConcreteComponent добавлена локальная переменная price, которая будет изменяться как в нем самом, так и декораторах. Имена классов (кроме постфиксов "A" и "B") совпадают с именами участников шаблона.
function Component() { this.operation = function() { }; this.getPrice = function() { }; this.setPrice = function() { }; } function ConcreteComponent() { var price = 10; this.operation = function() { price += 4; alert("ConcreteComponent.operation, price: "+ price); }; this.getPrice = function() { return price; }; this.setPrice = function(val) { price = val; }; } ConcreteComponent.prototype = new Component(); ConcreteComponent.prototype.constructor = ConcreteComponent; function Decorator() { var component; this.setComponent = function(val) { component = val; }; this.getComponent = function() { return component; }; this.operation = function() { component.operation(); }; this.getPrice = function() { return component.getPrice(); }; this.setPrice = function(val) { component.setPrice(val); }; } Decorator.prototype = new Component(); Decorator.prototype.constructor = Decorator; function ConcreteDecoratorA() { Decorator.call(this); var operation = this.operation; // ссылка на метод, определенный в Decorator this.operation = function() { this.setPrice(this.getPrice() + 3); alert("ConcreteDecoratorA.operation, price: "+ this.getPrice()); operation(); }; } function ConcreteDecoratorB() { var dublicate = this; // ссылка на инстанцирующийся объект (т.к. this может меняться) Decorator.call(this); var operation = this.operation; // ссылка на метод, определенный в Decorator this.operation = function() { this.setPrice(this.getPrice() + 1); alert("ConcreteDecoratorB.operation, price: "+ this.getPrice()); addedBehavior(); operation(); }; function addedBehavior() { dublicate.setPrice(dublicate.getPrice() + 2); alert("addedBehavior, price: "+ dublicate.getPrice()); } } // использование c = new ConcreteComponent(); d1 = new ConcreteDecoratorA(); d2 = new ConcreteDecoratorB(); alert("изначальная цена: " + c.getPrice()); // 10 d1.setComponent(c); d2.setComponent(d1); d2.operation(); alert("цена после преобразования: " + c.getPrice()); // 20
Пример реализации на VB.NET
Пример реализацииNamespace Decorator Class Program Shared Sub Main() ' Создание ConcreteComponent и двух декораторов Dim C As New ConcreteComponent() Dim D1 As New ConcreteDecoratorA() Dim D2 As New ConcreteDecoratorB() ' Ссылки декоратора D1.SetComponent(C) D2.SetComponent(D1) D2.Operation() ' Ожидание действий от пользователя Console.Read() End Sub End Class ''' <summary> ''' Component - компонент ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяем интерфейс для объектов, на которые могут быть динамически ''' возложены дополнительные обязанности;</lu> ''' </li> ''' </remarks> MustInherit Class Component Public MustOverride Sub Operation() End Class ''' <summary> ''' ConcreteComponent - конкретный компонент ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяет объект, на который возлагается дополнительные обязанности</lu> ''' </li> ''' </remarks> Class ConcreteComponent Inherits Component Public Overrides Sub Operation() Console.WriteLine("ConcreteComponent.Operation()") End Sub End Class ''' <summary> ''' Decorator - декоратор ''' </summary> ''' <remarks> ''' <li> ''' <lu>хранит ссылку на объект <see cref="Component"/> и определяет интерфейс, ''' соответствующий интерфейсу <see cref="Component"/></lu> ''' </li> ''' </remarks> MustInherit Class Decorator Inherits Component Protected component As Component Public Sub SetComponent(ByVal component As Component) Me.component = component End Sub Public Overrides Sub Operation() If component IsNot Nothing Then component.Operation() End If End Sub End Class ''' <summary> ''' ConcreteDecorator - конкретный декоратор ''' </summary> ''' <remarks> ''' <li> ''' <lu>возглагает дополнительные обязанности на компонент.</lu> ''' </li> ''' </remarks> Class ConcreteDecoratorA Inherits Decorator Private addedState As String Public Overrides Sub Operation() MyBase.Operation() addedState = "New State" Console.WriteLine("ConcreteDecoratorA.Operation()") End Sub End Class ' "ConcreteDecoratorB" Class ConcreteDecoratorB Inherits Decorator Public Overrides Sub Operation() MyBase.Operation() AddedBehavior() Console.WriteLine("ConcreteDecoratorB.Operation()") End Sub Private Sub AddedBehavior() End Sub End Class End Namespace
Пример реализации на Delphi
Пример реализацииprogram DecoratorPattern; {$APPTYPE CONSOLE} uses SysUtils, Classes; type TInterfaceComponent = class public procedure Operation; virtual; abstract; end; type TConcreteComponent = class(TInterfaceComponent) public procedure Operation; override; end; procedure TConcreteComponent.Operation; begin Write('l'); end; type TDecorator = class(TInterfaceComponent) private FComponent: TInterfaceComponent; public constructor Create(aComponent: TInterfaceComponent); end; constructor TDecorator.Create(aComponent: TInterfaceComponent); begin FComponent:= aComponent; end; type TBeforeDecorator = class(TDecorator) procedure Operation; override; end; procedure TBeforeDecorator.Operation; begin Write('He'); FComponent.Operation; end; type TAfterDecorator = class(TDecorator) procedure Operation; override; end; procedure TAfterDecorator.Operation; begin FComponent.Operation; Write('o, '); end; type TOverrideDecorator = class(TDecorator) procedure Operation; override; end; procedure TOverrideDecorator.Operation; begin Write('World!!!'); end; var vI: Integer; vComponent: TInterfaceComponent; begin vComponent:= TConcreteComponent.Create; with TList.Create do begin Add(TBeforeDecorator.Create(vComponent)); Add(TAfterDecorator.Create(vComponent)); Add(TOverrideDecorator.Create(vComponent)); for vI := 0 to Pred(Count) do with TInterfaceComponent(Items[vI]) do begin Operation; Free; end; end; vComponent.Free; ReadLn; end.
Литература
- Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. — ISBN 0-201-71594-5
Ссылки
- Паттерн Decorator (Декоратор) — назначение, описание, особенности и реализация на С++.
Шаблоны проектирования Основные Порождающие Структурные Адаптер • Выделение частного класса данных • Декоратор • Заместитель • Компоновщик • Мост • Приспособленец • Фасад
Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Категории:- Шаблоны проектирования
- Структурные шаблоны проектирования
Wikimedia Foundation. 2010.