在装饰器模式中添加状态

时间:2011-11-08 23:14:20

标签: 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方法并通过链传播,那一切都很好。但这对国家有何影响?我无法访问HotBigCheesyPizza上的size属性。

我错过了什么部分?错误的模式?

感谢您的帮助! 干杯

3 个答案:

答案 0 :(得分:3)

装饰器模式用于向装饰类添加其他行为,而无需客户端进行调整。因此,它不是要为正在装饰的东西添加新界面(例如hotnesscheese)。

它可能用于什么的一个不好的例子是您想要更改size的计算方式:您可以创建一个MetricSizePizzaDecorator,将大小转换为英制/公制单位。客户不会知道披萨已被装饰 - 它只是调用getSize()并做任何与结果有关的事情(例如,计算价格)。

我可能不会在我的例子中使用装饰器,但重点是:它不会改变界面。实际上,几乎所有的设计模式都归结为 - 在不改变界面的情况下为设计增加可变性。

答案 1 :(得分:1)

我相信你的组件Pizza和你的抽象装饰器PizzaDecorator应该共享相同的接口,这样装饰器的每个实例都能够与核心组件{{1}进行相同的操作}。

答案 2 :(得分:1)

添加状态的一种方法是使用自引用数据结构(列表)。但是这会使用访问者模式并且比你想要的更多。此代码从A little Java, a few patterns

重写
// 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");
        }
    }