当我装饰对象时,变量重置为默认值

时间:2015-12-02 16:06:27

标签: java enums decorator

我正在尝试学习装饰设计模式。在这个例子中,我创建了一个应用程序,根据其类型(浓缩咖啡,咖啡因,家庭混合),大小(高,大,通风)和调味品装饰(大豆,生奶油,蒸牛奶)计算咖啡的成本 - 为简洁起见,排除了一些代码。

正如您从底部的输出中看到的,如果我是setSize(GRANDE)并且没有包装对象,则getSize()返回GRANDE。

如果我使用setSize(GRANDE),然后装饰对象,则getSize()返回TALL(Beverage类中设置的默认值)。

如果我使用setSize(GRANDE),再次装饰对象setSize(GRANDE),则getSize()返回GRANDE。

注意:虽然尺寸打印不正确,但成本计算正确。

问题:有没有办法对此进行编码,所以当我使用setSize()时,它甚至在对象被装饰后仍保留该值?

package coffee;

public abstract class Beverage {
    String description = "Unknown Beverage";
    public enum Size { TALL, GRANDE, VENTI };
    Size size = Size.TALL;

    public String getDescription() {
        return description;
    }

    public void setSize(Size size) {
        this.size = size;
    }

    public Size getSize() {
        return size;
    }

    public abstract double cost();
}
package coffee;

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}
package coffee;

public class HouseBlend extends Beverage {

    public HouseBlend() {
        description = "House Blend Coffee";
    }

    public double cost(){
        return .89;
    }
}
package coffee;

public class Soy extends CondimentDecorator {
    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    public String getDescription() {
        return beverage.getDescription() + ", Soy";
    }

    public double cost() {
        double cost = beverage.cost();
        if( beverage.getSize() == Size.TALL) {
            cost += .10;
        } else if( beverage.getSize() == Size.GRANDE) {
            cost += .15;
        }else if( beverage.getSize() == Size.VENTI) {
            cost += .20;
        } 
        return cost;
    }   
}
package coffee;
import coffee.Beverage.Size;
public class StarbuzzCoffeeController {

    public static void main(String[] args) {
        Beverage beverage = new HouseBlend();
        System.out.println( beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost() ));
        beverage.setSize(Size.GRANDE);
        System.out.println( beverage.getSize() + " " + beverage.getDescription() + " $" + String.format("%.2f", beverage.cost() ));
        System.out.println("-----------------------");

        Beverage beverage2 = new HouseBlend();
        beverage2.setSize(Size.GRANDE);
        System.out.println( beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost() ));

如果我不跟随另一个setSize(),它会在下一行打印“TALL”:

        beverage2 = new Soy(beverage2); 
        System.out.println( beverage2.getSize() + " " + beverage2.getDescription() + " $" + String.format("%.2f", beverage2.cost() ));
        System.out.println("-----------------------");

        Beverage beverage3 = new HouseBlend();
        beverage3.setSize(Size.GRANDE);
        System.out.println( beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost() ));
        beverage3 = new Soy(beverage3);

如果我在装饰对象后再次使用setSize(),则大小会正确打印为GRANDE:

        beverage3.setSize(Size.GRANDE);
        System.out.println( beverage3.getSize() + " " + beverage3.getDescription() + " $" + String.format("%.2f", beverage3.cost() ));
    }
}

OUTPUT ::

TALL House混合咖啡$ 0.89

GRANDE House混合咖啡$ 0.89

-----------------------

GRANDE House混合咖啡$ 0.89

TALL House Blend Coffee,Soy $ 1.04

-----------------------

GRANDE House混合咖啡$ 0.89

GRANDE House混合咖啡,大豆1.04美元

4 个答案:

答案 0 :(得分:3)

您的类new DownloadImageTask(YourImageView) .execute(YourURLString); 不会覆盖Soy方法 - 因此,使用了基类的默认实现。基类返回自己的getSize()成员,该成员初始化为TALL。

要解决此问题,您必须在班级size中相应地覆盖getSize()方法:

Soy

这将正确返回装饰对象的大小。

答案 1 :(得分:1)

您的Decorator模式的实现在概念上是不正确的。您遇到的问题来自于错误的实施。

我在下面提到此维基百科文章中的5个项目:https://en.wikipedia.org/wiki/Decorator_pattern

  1. "对原始"组件"进行子类化进入一个装饰者"类;"
    • 在你的情况下"组件"是BeverageCondimentDecorator是装饰者
  2. "在Decorator类中,添加一个Component指针作为字段;"
    • 将组件指针添加为字段。您在Beverage beverage中添加了字段Soy,而不是将其添加到CondimentDecorator
  3. "将一个Component传递给Decorator构造函数以初始化Component指针;"
    • 这应该在CondimentDecorator以及
    • 中完成
  4. "在Decorator类中,重定向所有"组件" "组件"的方法指针"
    • 你还没有这样做,这是你问题的根源。在CondimentDecorator上,默认情况下,getSize()应致电beverage.getSize()。所有其他领域都应该这样做。
  5. 我认为你应该回到装饰器模式试图实现的基础知识,并在考虑正确的想法的情况下重建你的实现。您可能能够以更简单的方式修复问题,但您可能会错误地了解装饰器模式实际上是什么。

    请记住以下主要想法:

    • 饮料是您的组件,
    • CondimentDecorator是您的装饰者,
    • Soy是ConcreteDecorator

答案 2 :(得分:0)

我认为如果你想要"原始"的价值,你必须将getSize()委托给包装/装饰的实例。饮料

  Beverage cappuccino = new Cappuccino(Size.VENTI);
  Beverage soyMilkCappuccino = new Soy(cappuccino);

内部Soy

  class Soy extends Beverage {
       private final Beverage decorated;
       Soy(Beverage other) { this.decorated = other; }
       // delegation
       Size getSize() { return decorated.getSize(); }
  }

答案 3 :(得分:0)

以下修复了getSize()问题(我仍在努力了解如何重构此代码):

在CondimentDecorator类中添加了抽象方法getSize:

package coffee;

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
    public abstract Size getSize();   
}

在concreteDecorator类中覆盖getSize()。 e.g:

package coffee;

public class Soy extends CondimentDecorator {
    Beverage beverage;

    public Soy(Beverage beverage) {
        this.beverage = beverage;
    }

    public String getDescription() {
        return beverage.getDescription() + ", Soy";
    }

    public Size getSize() {
        return this.beverage.getSize();
    }

    public double cost() {
        double cost = beverage.cost();
        if( beverage.getSize() == Size.TALL) {
            cost += .10;
        } else if( beverage.getSize() == Size.GRANDE) {
            cost += .15;
        }else if( beverage.getSize() == Size.VENTI) {
            cost += .20;
        } 
        return cost;
    }   
}

并且还移动了setSize()命令,使其始终在装饰对象之前出现(因为调味品成本与尺寸相关,如果在装饰咖啡后更改尺寸,它将计算错误的价格):

public static void main(String[] args) {
    //...
    Beverage beverage4 = new Decaf();
    beverage4.setSize(Size.VENTI); //SET THE SIZE BEFORE DECORATING (COST OF CONDIMENTS DEPENDS ON SIZE)
    beverage4 = new SteamedMilk(beverage4);
    beverage4 = new Mocha(beverage4);
    beverage4 = new Whip(beverage4);
    //beverage4.setSize(Size.VENTI); //<--No, No. 
    System.out.println( beverage4.getSize() + " " + beverage4.getDescription() + " $" + String.format("%.2f", beverage4.cost() ));
}