Variable resets to default value when I decorate object
我正在学习装饰设计师的设计模式。在这个例子中,我创建了一个应用程序,它根据咖啡的类型(浓缩咖啡、脱咖啡因、家庭用混合咖啡)、大小(高、大、通风)和调味品装饰器(大豆、生奶油、蒸牛奶)来计算咖啡的成本——一些代码因简洁而被排除在外。
从底部的输出可以看到,如果我设置了大小(grande)并且不包装对象,getSize()将返回grande。
如果我设置了大小(grande),那么装饰对象,getSize()将返回tall(饮料类中设置的默认值)。
如果我设置(grande),装饰对象,再次设置(grande),那么getSize()返回grande。
注:虽然尺寸打印不正确,但成本计算正确。
问题:有没有一种方法可以这样编码:当我setSize()时,即使在对象被修饰之后,它仍然保持这个值?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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(); } |
1 2 3 4 5 | package coffee; public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); } |
1 2 3 4 5 6 7 8 9 10 11 12 | package coffee; public class HouseBlend extends Beverage { public HouseBlend() { description ="House Blend Coffee"; } public double cost(){ return .89; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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()执行此操作,它将"高"作为下一行的大小打印出来:
1 2 3 4 5 6 7 8 | 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); |
如果我在装饰完对象后再次设置了大小(),则大小将正确打印为Grande:
1 2 3 4 |
输出:
塔尔豪斯混合咖啡0.89美元
格兰德豪斯混合咖啡0.89美元
-----------------
格兰德豪斯混合咖啡0.89美元
高屋混合咖啡,大豆1.04美元
-----------------
格兰德豪斯混合咖啡0.89美元
格兰德豪斯混合咖啡,大豆1.04美元
类
为了解决这个问题,您必须在类
1 2 3 4 | @Override public Size getSize() { return this.beverage.getSize(); } |
这将正确返回装饰对象的大小。
您对decorator模式的实现在概念上是不正确的。您遇到的问题来自于不正确的实现。
我在下面引用维基百科文章中的5个项目:https://en.wikipedia.org/wiki/decorator_pattern
- 在您的例子中,"组件"是
Beverage ,CondimentDecorator 是修饰器。
- 将组件指针添加为字段。您在
Soy 中添加了字段Beverage beverage ,而不是添加到CondimentDecorator 中。
- 这也应该在
CondimentDecorator 中完成。
- 你没有这样做,这是你问题的根源。在
CondimentDecorator 上,默认情况下,getSize() 应调用beverage.getSize() 。其他所有字段也应如此。
我认为您应该回到decorator模式试图实现什么的基础上,并用正确的思想重新构建您的实现。您也许可以用一种更简单的方式解决您的问题,但是您可能对装饰器模式实际上是什么有一个错误的想法。
请记住以下主要观点:
- 饮料是你的组成部分,
- 调味品制作者是你的装饰师,
- 大豆是一种混凝土装饰材料。
下面修复了getSize()问题(我仍在努力了解如何重构此代码):
已将抽象方法getSize添加到ConditionTDecorator类:
1 2 3 4 5 6 | package coffee; public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); public abstract Size getSize(); } |
在ConcreteDecorator类中覆盖GetSize()。例如。:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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()命令,使其始终出现在对象被修饰之前(因为调味品成本与大小有关,所以如果您在咖啡被修饰之后更改大小,它将计算错误的价格):
1 2 3 4 5 6 7 8 9 10 | 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() )); } |
如果你想得到"原汁原味"饮料的价值,我想你必须委托
1 2 | Beverage cappuccino = new Cappuccino(Size.VENTI); Beverage soyMilkCappuccino = new Soy(cappuccino); |
在
1 2 3 4 5 6 | class Soy extends Beverage { private final Beverage decorated; Soy(Beverage other) { this.decorated = other; } // delegation Size getSize() { return decorated.getSize(); } } |