Зачем нам нужен декоратор в шаблоне проектирования декоратора?

Предположим, у меня есть класс с именем A, и я хочу использовать шаблон проектирования декоратора. Поправьте меня, если я ошибаюсь, но чтобы это работало, нам нужно создать класс декоратора, скажем, ADecorator, который будет содержать ссылку на экземпляр A, а все остальные декораторы будут расширять его для добавления функциональности.

Я не понимаю, почему мы должны создавать класс декоратора вместо использования экземпляра A?


person Geo    schedule 07.11.2008    source источник


Ответы (3)


Шаблон декоратора используется для динамического добавления возможностей к объектам (то есть во время выполнения). Обычно возможности объекта фиксируются при написании класса. Но важным моментом является то, что функциональность объекта расширяется таким образом, что это прозрачно для клиента объекта, поскольку он реализует тот же интерфейс, что и исходный объект, делегируя ответственность декорированному объекту.

Шаблон декоратора работает в сценариях, где объект может иметь множество дополнительных функций. Без шаблона декоратора вам придется создавать разные классы для каждой конфигурации объекта-параметра. Один очень полезный пример взят из книги О'Рейли Head First Design Patterns. Он использует пример кофейни, который звучит так же, как StarBucks.

Итак, у вас есть базовый кофе с таким методом, как стоимость.

public double cost(){
     return 3.45;
}

Затем клиент может добавить сливки, которые стоят 0,35, поэтому теперь вы создаете класс CoffeeCream с методом стоимости:

public double cost(){
    return 3.80;
}

Тогда покупатель может захотеть Мокко, который стоит 0,5, и он может захотеть Мокко со сливками или Мокко без сливок. Итак, вы создаете классы CoffeeMochaCream и CoffeeMocha. Затем клиент хочет двойные сливки, поэтому вы создаете класс CoffeeCreamCream… и т. д. В итоге вы получаете взрыв класса. Пожалуйста, извините за неудачный пример. Это немного поздно, и я знаю, что это тривиально, но это выражает суть.

Вместо этого вы можете создать абстрактный класс Item с абстрактным методом стоимости:

public abstract class Item{
    public abstract double cost();
}

И вы можете создать конкретный класс Coffee, который расширяет Item:

public class Coffee extends Item{
    public double cost(){
       return 3.45;
    }
}

Затем вы создаете CoffeeDecorator, который расширяет тот же интерфейс и содержит Item.

public abstract class CoffeeDecorator extends Item{
     private Item item;
     ...
}

Затем вы можете создать конкретные декораторы для каждого варианта:

public class Mocha extends CoffeeDecorator{

   public double cost(){
     return item.cost() + 0.5;
   }

}

Обратите внимание, что декоратору все равно, какой тип объекта он упаковывает, лишь бы это был Item? Он использует функцию cost() объекта item и просто добавляет свою стоимость.

public class Cream extends CoffeeDecorator{

   public double cost(){
     return item.cost() + 0.35;
   }

}

Теперь возможно большое количество конфигураций с этими несколькими классами: например.

 Item drink = new Cream(new Mocha(new Coffee))); //Mocha with cream

or

 Item drink = new Cream(new Mocha(new Cream(new Coffee))));//Mocha with double cream

И так далее.

person Vincent Ramdhanie    schedule 08.11.2008
comment
Мне этот ответ легче понять, чем статью в Википедии. - person Lawrence Dol; 08.11.2008
comment
@dhiller Ага. Шаблоны проектирования были первыми, которые я попробовал, и я начал рекомендовать их всем своим ученикам. - person Vincent Ramdhanie; 08.11.2008
comment
Этот пример легче понять, хотя я не думаю, что это правильная проблема, которую следует решать с помощью этого шаблона. - person noob.spt; 16.11.2009

Кстати, если вы только начинаете изучать шаблоны, книга Head First Design Patterns феноменальна. Это действительно упрощает усвоение концепций и позволяет сопоставлять и сравнивать похожие шаблоны таким образом, что это смехотворно легко понять.

person Spencer Kormos    schedule 08.11.2008
comment
Я должен второй это, книга невероятна. На самом деле приведенный выше пример с кофе взят из этой книги, но книга развивает этот пример намного дальше. - person P Arrayah; 08.11.2008

В некоторых языках (таких как Ruby или JavaScript) вы можете просто добавить новую функциональность в экземпляр A. Я заметил, что ваш вопрос помечен как Java, поэтому я предполагаю, что вы спрашиваете, почему вы не можете сделать это на Java. Причина в том, что Java статически типизирована. Экземпляр A может иметь только те методы, которые класс A определяет или наследует. Следовательно, если вы хотите во время выполнения предоставить экземпляру A метод, который A не определяет, этот новый метод должен быть определен в другом классе.

person Andru Luvisi    schedule 08.11.2008