装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更具有弹性的替代方案。书中给出的是一个星巴兹的实例,他们准备更新订单系统,原先的类设计如下:
现在要实现,当客户购买咖啡时,可以要求加入各种调料,例如Milk、Soy、Mocha等,然后加入的调料会收取不同的费用。我们可能会按如下方法去尝试:
为每一种咖啡都建一个类,但是这样就会出现"类爆炸"的问题了。于是你可能想到要通过修改Beverage:
public abstract class Beverage {
String description = "Unknown Beverage";
boolean milk;
boolean soy;
boolean mocha;
boolean whip;
public boolean hasMilk();
public void setMilk();
public boolean hasSoy();
public void setSoy();
public boolean hasMocha();
public void setMocha();
public boolean hasWhip();
public void setWhip();
public String getDescription() {
return description;
}
public double cost();
}
这个时候cost()不是一个抽象方法,让它计算要加入的各种调料的价钱,然后子类仍将覆盖cost(),但是会调用超类的cost(),计算出基本饮料加上调料的价钱。但是这样又会出现几个问题:
1、一旦出现新的调料,我们就要加上新的方法,并改变超类的cost()方法。
2、以后可能会开发出新饮料,新的饮料某些调料可能不适合,但是这个设计方式,新的子类仍将要继承那些不适合的方法。
3、万一顾客想要双倍摩卡咖啡呢。
下面采用新的方法:以饮料为主题,然后在运行时以调料来“装饰”饮料。用代码的方式呈现如下:
先从Beverage类下手:
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
然后实现Condiment(调料)抽象类,也就是装饰者类:
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
接下来写饮料的代码,例如浓缩咖啡(Espresso),家常咖啡(HouseBlend):
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
public double cost() {
return 1.99;
}
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
public double cost() {
return 0.89;
}
}
写调料代码,例如摩卡(Mocha):
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public double cost() {
//这里计算带Mocha饮料的价钱,先把调用委托给被装饰对象,再加上Mocha的价钱,得到最终结果。
return 0.20 + beverage.cost();
}
}
最后,我们可以通过下面的方式来使用:
public class StarBuzzCoffee {
public static void main(String args[]) {
//订一杯Espresso,不需要调料。
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + "$" + beverage.cost());
//订一杯DarkRost咖啡。
Beverage beverage2 = new DarkRost();
//用Mocha装饰它。
beverage2 = new Mocha(beverage2);
//用第二个Mocha装饰它。
beverage2 = new Mocha(beverage2);
//用Whip装饰它。
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription() + "$" + beverage2.cost());
//订一杯HouseBlend咖啡
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription() + "$" + beverage3.cost());
}
}
这一章提到的设计原则:
1、开放-关闭原则(类应该对扩展开放,对修改关闭)。