Декоратор (шаблон проектирования)
Материал из Seo Wiki - Поисковая Оптимизация и Программирование
Название | декоратор |
---|---|
Английское название | decorator |
Диаграмма | |
Тип | структурный |
Назначение | для динамического подключения к объекту дополнительных обязательств
|
Плюсы |
|
Родственные шаблоны | Фасад, Адаптер |
Декоратор, Decorator — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту. Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
Известен также под менее распространённым названием Обёртка (Wrapper), которое во многом раскрывает суть реализации шаблона.
Содержание |
Основные характеристики
Задача
Объект, который предполагается использовать, выполняет основные функции. Однако может потребоваться добавить к нему некоторую дополнительную функциональность, которая будет выполняться до, после или даже вместо основной функциональности объекта.
Способ решения
Декоратор предусматривает расширение функциональности объекта без определения подклассов.
Участники
Класс ConcreteComponent
— класс, в который с помощью шаблона Декоратор добавляется новая функциональность. В некоторых случаях базовая функциональность предоставляется классами, производными от класса ConcreteComponent
. В подобных случаях класс ConcreteComponent
является уже не конкретным, а абстрактным. Абстрактный класс Component
определяет интерфейс для использования всех этих классов.
Следствия
1. Добавляемая функциональность реализуется в небольших объектах. Преимущество состоит в возможности динамически добавлять эту функциональность до или после основной функциональности объекта ConcreteComponent
.
2. Позволяет избегать перегрузки функциональными классами на верхних уровнях иерархии
3. Декоратор и его компоненты не являются идентичными
Реализация
Создается абстрактный класс, представляющий как исходный класс, так и новые, добавляемые в класс функции. В классах-декораторах новые функции вызываются в требуемой последовательности — до или после вызова последующего объекта.
При желании остаётся возможность использовать исходный класс (без расширения функциональности), если на его объект сохранилась ссылка.
Замечания и комментарии
- Хотя объект-декоратор может добавлять свою функциональность до или после функциональности основного объекта, цепочка создаваемых объектов всегда должна заканчиваться объектом класса
ConcreteComponent
. - Базовые классы языка Java широко используют шаблон Декоратор для организации обработки операций ввода-вывода.
Применение шаблона
Пример реализации шаблона на 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(); } } /// <summary> /// Component - компонент /// </summary> /// <remarks> /// <li> /// <lu>определяем интерфейс для объектов, на которые могут быть динамически /// возложены дополнительные обязанности;</lu> /// </li> /// </remarks> abstract class Component { public abstract void Operation(); } /// <summary> /// ConcreteComponent - конкретный компонент /// </summary> /// <remarks> /// <li> /// <lu>определяет объект, на который возлагается дополнительные обязанности</lu> /// </li> /// </remarks> class ConcreteComponent : Component { public override void Operation() { Console.WriteLine("ConcreteComponent.Operation()"); } } /// <summary> /// Decorator - декоратор /// </summary> /// <remarks> /// <li> /// <lu>хранит ссылку на объект <see cref="Component"/> и определяет интерфейс, /// соотведствующий интерфейсу <see cref="Component"/></lu> /// </li> /// </remarks> abstract class Decorator : Component { protected Component component; public void SetComponent(Component component) { this.component = component; } public override void Operation() { if (component != null) { component.Operation(); } } } /// <summary> /// ConcreteDecorator - конкретный декоратор /// </summary> /// <remarks> /// <li> /// <lu>возглагает дополнительные обязанности на компонент.</lu> /// </li> /// </remarks> 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: typedef std::auto_ptr<IComponent> SPtr; virtual void operation() = 0; }; class Component: public IComponent{ public: virtual void operation(){ std::cout<<"World!"<<std::endl; } }; class DecoratorOne: public IComponent{ SPtr _component; public: DecoratorOne(SPtr& component): _component(component) {} virtual void operation(){ std::cout<<", "; _component->operation(); } }; class DecoratorTwo: public IComponent{ SPtr _component; public: DecoratorTwo(SPtr& component): _component(component) {} virtual void operation(){ std::cout<<"Hello"; _component->operation(); } }; int main(){ IComponent::SPtr component(new Component); DecoratorTwo obj(IComponent::SPtr(new DecoratorOne(component))); obj.operation(); // prints "Hello, World!\n" return 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();
Примеры реализации на 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
Литература
- Алан Шаллоуей, Джеймс Р. Тротт Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию = Design Patterns Explained: A New Perspective on Object-Oriented Design. — М.: «Вильямс», 2002. — С. 288. — ISBN 0-201-71594-5
Файл:Soft template.gif | Это незавершённая статья о программном обеспечении. Вы можете помочь проекту, исправив и дополнив её. |
структурные шаблоны проектирования |
адаптер | мост | компоновщик | декоратор | фасад | заместитель | приспособленец | Выделение частного класса данных |
de:Decorator en:Decorator pattern es:Decorator (patrón de diseño) fr:Décorateur (patron de conception) it:Decorator ja:Decorator パターン ko:Decorator 패턴 nl:Decorator pl:Dekorator (wzorzec projektowy) uk:Декоратор (шаблон проектування) vi:Decorator pattern zh:修饰模式
→