Когда использовать шаблон декоратора?
Я просматриваю свои шаблоны проектирования, и один образец, который мне еще предстоит серьезно использовать в моем кодировании, - это шаблон Decorator.
Я понимаю шаблон, но то, что я хотел бы знать, - это некоторые хорошие конкретные примеры раз в реальном мире, что образец декоратора - лучшее/оптимальное/элегантное решение. Конкретные ситуации, когда потребность в шаблоне декоратора действительно удобна.
Спасибо.
Ответы
Ответ 1
Шаблон декоратора часто используется для потоков: вы можете обтекать поток потоком, чтобы получить добавленную функциональность. Я видел это с картой .Net - насколько я знаю, это происходит в другом месте. Мой любимый использует GZipStream вокруг FileStream для дополнительного сжатия.
Ответ 2
Шаблон Decorator используется для добавления дополнительных функций к определенному объекту, а не к классу объектов. Легко добавить функциональность ко всему классу объектов путем подклассификации объекта, но невозможно таким образом расширить один объект. С помощью шаблона Decorator вы можете добавить функциональность к одному объекту и оставить другие без изменений.
В Java классическим примером шаблона декоратора является реализация потоков ввода-вывода Java.
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
Предыдущий код создает читатель - lrdr
- который читает из файла и отслеживает номера строк. Строка 1 создает устройство чтения файлов (frdr
), а строка 2 добавляет отслеживание номера строки.
На самом деле, я настоятельно рекомендую вам посмотреть исходный код Java для классов ввода/вывода Java.
Ответ 3
Недавно я использовал шаблон декоратора в веб-службе, которая использует следующий интерфейс CommandProcessor:
public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
В принципе, CommandProcessor получает запрос и создает надлежащую команду, выполняет команду и создает соответствующий ответ и отправляет ответ. Когда я захотел добавить время и зарегистрировать его, я создал TimerDecorator, который использовал существующий CommandProcessor в качестве своего компонента. TimerDecorator реализует интерфейс CommandProcessor, но просто добавляет время, а затем вызывает его цель, которая является настоящим CommandProcessor. Что-то вроде этого:
public class TimerDecorator implements CommandProcessor {
private CommandProcessor target;
private Timer timer;
public TimerDecorator(CommandProcessor processor) {
this.target = processor;
this.timer = new Timer();
}
public Command receive(Request request) {
this.timer.start();
return this.target.receive(request);
}
public Response execute(Command command) {
return this.target.execute(command);
}
public void respond(Response response) {
this.target.response(response);
this.timer.stop();
// log timer
}
}
Таким образом, настоящий CommandProcessor обернут внутри TimerDecorator, и я могу обрабатывать TimerDecorator так же, как и целевой CommandProcessor, но теперь добавлена логика синхронизации.
Ответ 4
Decorator шаблон динамически изменяет функциональность объекта во время выполнения без ущерба для существующей функциональности объектов.
Ключевые варианты использования:
- Динамически добавлять дополнительные функции/обязанности
- Устранение функциональности/ответственности динамически
- Избегайте слишком большого количества субклассификации для добавления дополнительных обязанностей.
Недостатки:
- Чрезмерное использование принципа Open Closed (Открыть для расширения и закрыто для модификации). Используйте эту функцию экономно, где код менее всего изменился.
- Слишком много небольших классов и добавит накладные расходы на обслуживание.
Пример реального мира: Вычислить цену напитка, который может содержать несколько ароматов.
abstract class Beverage {
protected String name;
protected int price;
public Beverage(){
}
public Beverage(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
protected void setPrice(int price){
this.price = price;
}
protected int getPrice(){
return price;
}
protected abstract void decorateBeverage();
}
class Tea extends Beverage{
public Tea(String name){
super(name);
setPrice(10);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
class Coffee extends Beverage{
public Coffee(String name){
super(name);
setPrice(15);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
abstract class BeverageDecorator extends Beverage {
protected Beverage beverage;
public BeverageDecorator(Beverage beverage){
this.beverage = beverage;
setName(beverage.getName()+"+"+getDecoratedName());
setPrice(beverage.getPrice()+getIncrementPrice());
}
public void decorateBeverage(){
beverage.decorateBeverage();
System.out.println("Cost of:"+getName()+":"+getPrice());
}
public abstract int getIncrementPrice();
public abstract String getDecoratedName();
}
class SugarDecorator extends BeverageDecorator{
public SugarDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateSugar();
}
public void decorateSugar(){
System.out.println("Added Sugar to:"+beverage.getName());
}
public int getIncrementPrice(){
return 5;
}
public String getDecoratedName(){
return "Sugar";
}
}
class LemonDecorator extends BeverageDecorator{
public LemonDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateLemon();
}
public void decorateLemon(){
System.out.println("Added Lemon to:"+beverage.getName());
}
public int getIncrementPrice(){
return 3;
}
public String getDecoratedName(){
return "Lemon";
}
}
public class VendingMachineDecorator {
public static void main(String args[]){
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
}
}
выход:
Cost of:Assam Tea:10
Cost of:Assam Tea+Lemon:13
Added Lemon to:Assam Tea
Cost of:Assam Tea+Lemon+Sugar:18
Added Sugar to:Assam Tea+Lemon
Cost of:Cappuccino:15
Cost of:Cappuccino+Lemon:18
Added Lemon to:Cappuccino
Cost of:Cappuccino+Lemon+Sugar:23
Added Sugar to:Cappuccino+Lemon
В этом примере вычисляется стоимость напитка в торговом автомате после добавления многих вкусов в напиток.
В приведенном выше примере:
Стоимость чая = 10, лимон = 3 и сахар = 5. Если вы делаете сахар + лимон + чай, он стоит 18.
Стоимость кофе = 15, лимон = 3 и сахар = 5. Если вы делаете сахар + лимон + кофе, он стоит 23
Используя тот же Decorator для обоих напитков (Tea and Coffee), количество подклассов было уменьшено. В отсутствие шаблона Decorator у вас должны быть разные подклассы для разных комбинаций.
Комбинации будут такими:
SugarLemonTea
SugarTea
LemonTea
SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino
и т.д..
Используя тот же Decorator для обоих напитков, количество подклассов было уменьшено. Это возможно благодаря использованию композиции, а не наследования, используемой в этом шаблоне.
Связанный вопрос SE:
Рисунок декоратора для IO
Полезные ссылки:
design-patterns-decorator по dzone
decorator с помощью sourcemaking
oodesign статья
Ответ 5
Декоратор прост, но чрезвычайно мощный. Это ключ к достижению разделения интересов и важный инструмент для открытого закрытого принципа. Возьмите общий пример размещения заказа на товар:
IOrderGateway
{
void PlaceOrder(Order order);
{
Основная реализация: AmazonAffiliateOrderGateway
Возможные декораторы могут быть:
-
IncrementPerformanceCounterOrderGateway
-
QueueOrderForLaterOnTimeoutOrderGateway
-
EmailOnExceptionOrderGateway
-
InterceptTestOrderAndLogOrderGateway
Более подробный пример отсюда иллюстрирует декоратор, который сохраняет контакты для заказов, которые были созданы с использованием подарочной карты при выполнении заказа:
class OrderWithGiftCardGateway extends OrderGatewayDecorator
{
...
public function createOrder(CreateOrderRequest $order)
{
if ($this->containsGiftCard($order))
{
$this->addContactToFolder($order);
}
return parent::createOrder($order);
}
}
Ответ 6
Zend Framework использует декоратор для элементов формы
Дополнительная информация: http://framework.zend.com/manual/en/zend.form.decorators.html
Ответ 7
- добавить обязанности к отдельным объектам динамически и прозрачно.
- за обязанности, которые могут быть сняты.
- когда расширение путем подклассификации нецелесообразно. Иногда возможно большое количество независимых расширений, что приведет к взрыву подклассов для поддержки каждой комбинации.
Ответ 8
Рисунок декоратора используется языком С#. Он используется, чтобы украсить класс ввода/вывода потока С#. Украшенные версии - это BufferedStream, FileStrem, MemoryStrem, NetworkStream и CryptoStream.
Эти подклассы наследуются от класса Stream и также содержат экземпляр класса Stream.
Подробнее здесь