Декоратор (шаблон проектирования)

Материал из Википедии — свободной энциклопедии

Перейти к: навигация, поиск
Название декоратор
Английское название decorator
Диаграмма
Представление структуры шаблона Декоратор
Тип структурный
Назначение для динамического подключения к объекту дополнительных обязательств


Плюсы  
  • нет необходимости создавать подклассы для расширения функциональности объекта;
  • возможность динамически подключать новую функциональность до или после основной функциональности объекта ConcreteComponent.
Родственные шаблоны Фасад, Адаптер

Декоратор, 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();
    }
  }
 
  // "Component"
 
  abstract class Component
  {
    public abstract void Operation();
  }
 
  // "ConcreteComponent"
 
  class ConcreteComponent : Component
  {
    public override void Operation()
    {
      Console.WriteLine("ConcreteComponent.Operation()");
    }
  }
 
  // "Decorator"
 
  abstract class Decorator : Component
  {
    protected Component component;
 
    public void SetComponent(Component component)
    {
      this.component = component;
    }
 
    public override void Operation()
    {
      if (component != null)
      {
        component.Operation();
      }
    }
  }
 
  // "ConcreteDecoratorA"
 
  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()
    {
    }
  }
}

[править] Пример реализации на 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



структурные шаблоны проектирования

адаптер | мост | компоновщик | декоратор | фасад | заместитель | приспособленец