《Head First Design Pattern》书中关于 装饰者模式 是通过一个例子来引入的。 该例子为,若需要你为星巴兹咖啡设计订单系统,应该如何设计?
其中的错误例子将有继承以及组合的形式来实现,就是所有的咖啡类将继承Beverage这个饮料类。
其中这个饮料类中包括了cost()和getDescription()两个方法(如下图),看起来似乎没有什么问题,
但是当该系统需要扩展(比如像要引入调料(牛奶、豆浆、摩卡、奶泡))的时候,就遇到问题了。
因为通过组合的形式,Beverage类中将会引入所有的调料参数和调料的has()和set()方法,
将会使整个类变得难以理解以及修改。不仅如此,当客户需要双倍摩卡的时候,这种形式将不能实现。

在遵循 类应该对扩展开放,对修改关闭 的设计原则之下,书中引入了 装饰者模式 这种设计模式, 其设计思路就是通过将装饰者(该例子是调料)和被装饰者(咖啡)设计为相同的超类型,然后装饰者可以 通过包装装饰者(通过依赖注入引入进类中),然后在再上自己的行为(在咖啡的价格/解释上加上调料的价格/解释)。
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
对以上例子的设计可以通过以下代码来理解:
1
2
3
4
5
6
7
8
public abstract class Beverage { // 将Beverage设计成抽象类
String description = "Unknown Beverage";
public String getDescription() { return description; }
public String toString() {
return "Description: " + getDescription() + ", Cost: " + cost();
}
public abstract double cost(); // 抽象方法,继承且不是抽象的方法必须实现
}
1
2
3
4
5
6
7
8
public class Espresso extends Beverage{
public Espresso() { description = "Espresso"; }
@Override
public double cost() { return 1.99; }
public static void main(String []args) {
System.out.println(new Espresso().getDescription());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Mocha extends CondimentDecorator {
private Beverage beverage;
public Mocha(Beverage b) { beverage = b; }
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
@Override
public double cost() { return beverage.cost() + .20; }
public static void main(String []args) {
Beverage espresso = new Espresso();
espresso = new Mocha(espresso);
espresso = new Mocha(espresso);./
System.out.println(espresso);
}
}

从以上代码以及类图可以容易理解将调料和咖啡继承相同的父类,并且通过调料中的构造函数引入咖啡的对象, 然后在咖啡的基础上加入自己的价格和描述。
通过上述的描述可以理解到生活中吃拉面需要添加温泉蛋、竹笋等配料的例子该如何设计。
更重要的是,java.io包中就大量的使用了 装饰者模式。下面的类图可以将FileInputStream、
StringBufferInputStream、ByteArrayInputStream看成是咖啡(被装饰类),将FilterInputStream
看成是调料(装饰类),他们都有相同的超类InputStream。而PushbackInputStream等就是具体的装饰者,
他们在被装饰类中添加了自己的”能力”。(扩展行为 比如像LineNumberInputStream就为其加上了计算行数的能力)

Summary
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
- 除了继承,装饰者模式也可以让我们扩展行为。
- 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂。