我正在尝试学习装饰设计模式。在这个例子中,我创建了一个应用程序,根据其类型(浓缩咖啡,咖啡因,家庭混合),大小(高,大,通风)和调味品装饰(大豆,生奶油,蒸牛奶)计算咖啡的成本 - 为简洁起见,排除了一些代码。
正如您从底部的输出中看到的,如果我是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美元
答案 0 :(得分:3)
您的类new DownloadImageTask(YourImageView)
.execute(YourURLString);
不会覆盖Soy
方法 - 因此,使用了基类的默认实现。基类返回自己的getSize()
成员,该成员初始化为TALL。
要解决此问题,您必须在班级size
中相应地覆盖getSize()
方法:
Soy
这将正确返回装饰对象的大小。
答案 1 :(得分:1)
您的Decorator模式的实现在概念上是不正确的。您遇到的问题来自于错误的实施。
我在下面提到此维基百科文章中的5个项目:https://en.wikipedia.org/wiki/Decorator_pattern
Beverage
,CondimentDecorator
是装饰者Beverage beverage
中添加了字段Soy
,而不是将其添加到CondimentDecorator
CondimentDecorator
以及CondimentDecorator
上,默认情况下,getSize()
应致电beverage.getSize()
。所有其他领域都应该这样做。我认为你应该回到装饰器模式试图实现的基础知识,并在考虑正确的想法的情况下重建你的实现。您可能能够以更简单的方式修复问题,但您可能会错误地了解装饰器模式实际上是什么。
请记住以下主要想法:
答案 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() ));
}