#oop #design-patterns #implementation #decorator #state
#ооп #шаблоны проектирования #реализация #декоратор #состояние
Вопрос:
Интересно, как добавить состояние в цепочку декораторов, которые будут доступны потребителю. Учитывая эту упрощенную модель:
abstract class AbstractPizza
{
public abstract print(...);
}
class Pizza : AbstractPizza
{
public int Size { get; set; }
public print(...);
}
abstract class AbstractPizzaDecorator
{
public Pizza:AbstractPizza;
public abstract print();
}
class HotPizzaDecorator : AbstractPizzaDecorator
{
public int Hotness { get; set; }
public print(...);
}
class CheesyPizzaDecorator : AbstractPizzaDecorator
{
public string Cheese { get; set; }
public print(...);
}
void Main()
{
BigPizza = new Pizza();
BigPizza.Size = 36;
HotBigPizza = new HotPizzaDecorator();
HotBigPizza.Pizza = BigPizza;
HotBigPizza.Hotness = 3;
HotBigCheesyPizza = new CheesyPizzaDecorator();
HotBigCheesyPizza.Pizza = HotBigPizza;
HotBigCheesyPizza.Cheese = "Blue";
HotBigCheesyPizza.print();
HotBigCheesyPizza.size = 28; // ERRRRRR !
}
Теперь, если все они реализуют метод print и распространяют его по цепочке, все хорошо. Но как это работает для состояния? Я не могу получить доступ к свойству size в HotBigCheesyPizza.
Какую часть я упускаю? Неправильный шаблон?
Спасибо за помощь! Приветствия
Комментарии:
1. Как вы собираетесь создавать HotBigCheesyPizza, если ваш HotPizzaDecorator не принимает CheesyPizzaDecorator… Не могли бы вы показать пример?
Ответ №1:
Шаблон декоратора предназначен для добавления дополнительного поведения к оформленному классу без необходимости настройки клиентом. Таким образом, он не предназначен для добавления нового интерфейса (например, hotness
, cheese
) к оформляемому объекту.
Несколько неудачный пример того, для чего это может быть использовано, — это когда вы хотите изменить способ size
вычисления: вы могли бы создать MetricSizePizzaDecorator
, который преобразует размер в / из английских / метрических единиц. Клиент не будет знать, что пицца была украшена — он просто звонит getSize()
и делает все, что ему нужно, с результатом (например, для расчета цены).
Я бы, вероятно, не стал использовать декоратор в моем примере, но суть в том, что он не изменяет интерфейс. Фактически, почти все шаблоны проектирования сводятся к этому — добавлению вариативности в дизайн без изменения интерфейсов.
Ответ №2:
одним из способов добавления состояния является использование самореферентной структуры данных (списка). но это использует шаблон посетителя и делает больше, чем вы, вероятно, хотите. этот код переписан с небольшого Java, нескольких шаблонов
// a self referential data structure with different types of nodes
abstract class Pie
{
abstract Object accept(PieVisitor ask);
}
class Bottom extends Pie
{
Object accept(PieVisitor ask) { return ask.forBottom(this); }
public String toString() { return "crust"; }
}
class Topping extends Pie
{
Object topping;
Pie rest;
Topping(Object topping,Pie rest) { this.topping=topping; this.rest=rest; }
Object accept(PieVisitor ask) { return ask.forTopping(this); }
public String toString() { return topping " " rest.toString(); }
}
//a class to manage the data structure
interface PieManager
{
int addTopping(Object t);
int removeTopping(Object t);
int substituteTopping(Object n,Object o);
int occursTopping(Object o);
}
class APieManager implements PieManager
{
Pie p=new Bottom();
// note: any object that implements a rational version of equal() will work
public int addTopping(Object t)
{
p=new Topping(t,p);
return occursTopping(t);
}
public int removeTopping(Object t)
{
p=(Pie)p.accept(new RemoveVisitor(t));
return occursTopping(t);
}
public int substituteTopping(Object n,Object o)
{
p=(Pie)p.accept(new SubstituteVisitor(n,o));
return occursTopping(n);
}
public int occursTopping(Object o)
{
return ((Integer)p.accept(new OccursVisitor(o))).intValue();
}
public String toString() { return p.toString(); }
}
//these are the visitors
interface PieVisitor
{
Object forBottom(Bottom that);
Object forTopping(Topping that);
}
class OccursVisitor implements PieVisitor
{
Object a;
OccursVisitor(Object a) { this.a=a; }
public Object forBottom(Bottom that) { return new Integer(0); }
public Object forTopping(Topping that)
{
if(that.topping.equals(a))
return new Integer(((Integer)(that.rest.accept(this))).intValue() 1);
else return that.rest.accept(this);
}
}
class SubstituteVisitor implements PieVisitor
{
Object n,o;
SubstituteVisitor(Object n,Object o) { this.n=n; this.o=o; }
public Object forBottom(Bottom that) { return that; }
public Object forTopping(Topping that)
{
if(o.equals(that.topping))
that.topping=n;
that.rest.accept(this);
return that;
}
}
class RemoveVisitor implements PieVisitor
{
Object o;
RemoveVisitor(Object o) { this.o=o; }
public Object forBottom(Bottom that) { return new Bottom(); }
public Object forTopping(Topping that)
{
if(o.equals(that.topping))
return that.rest.accept(this);
else return new Topping(that.topping,(Pie)that.rest.accept(this));
}
}
public class TestVisitor
{
public static void main(String[] args)
{
// make a PieManager
PieManager pieManager=new APieManager();
// add some toppings
pieManager.addTopping(new Float(1.2));
pieManager.addTopping(new String("cheese"));
pieManager.addTopping(new String("onions"));
pieManager.addTopping(new String("cheese"));
pieManager.addTopping(new String("onions"));
pieManager.addTopping(new String("peperoni"));
System.out.println("pieManager=" pieManager);
// substitute anchovies for onions
int n=pieManager.substituteTopping(new String("anchovies"),new String("onions"));
System.out.println(n " pieManager=" pieManager);
// remove the 1.2's
n=pieManager.removeTopping(new Float(1.2));
System.out.println(n " pieManager=" pieManager);
// how many anchovies do we have?
System.out.println(pieManager.occursTopping(new String("anchovies")) " anchovies");
}
}
Ответ №3:
Я полагаю, что ваш компонент Pizza
и ваш абстрактный декоратор PizzaDecorator
должны использовать один и тот же интерфейс, таким образом, каждый экземпляр декоратора способен выполнять те же операции, что и основной компонент Pizza
.
Комментарии:
1. это означало бы, что все декораторы реализуют одни и те же свойства. Я пытаюсь добавить состояние. HotPizzaDecorator не должен знать о размере, в то время как CheesyPizza не должен знать о горячести.