Зачем нам нужен декоратор в шаблоне оформления декоратора? - PullRequest
13 голосов
/ 08 ноября 2008

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

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

Ответы [ 4 ]

25 голосов
/ 08 ноября 2008

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

Шаблон декоратора работает в сценариях, где существует множество дополнительных функций, которые может иметь объект. Без шаблона декоратора вам придется создавать разные классы для каждой конфигурации опций объекта. Один очень полезный пример можно найти в книге О'Рейли 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, который расширяет Предмет:

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

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

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

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

public class Mocha extends CoffeeDecorator{

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

}

Заметьте, как декоратору все равно, к какому типу объекта он относится, если он является Предметом? Он использует 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

или

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

и т. Д.

5 голосов
/ 08 ноября 2008

Я не могу объяснить это лучше, чем статья Википедия .

3 голосов
/ 08 ноября 2008

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

2 голосов
/ 08 ноября 2008

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

...