10分钟一个设计模式系列
The Decorator Pattern
1.Basic 基础
为何称这个模式为Decorator模式呢。我们先来看一个例子,一家咖啡店,有很多种类型的饮品,如DarkRoast, HouseBlend, Espresso等等。不同类型的饮品,还能加入不同的调料,例如牛奶,Mocha, 大豆等等。那么这样这些饮品将会有多种各式各样的组合类型。
那么在设计的时候,不容置疑需要一个类作为各种饮品的基类,这个类我们可以命名为Beverage,Beverage类有cost()方法以及getDescription()方法,分别获得价格以及饮品描述,但如何实现各个子类呢?难道每个特定种类的饮料都要涉及一个子类么?这会引发“类爆炸”(class explosion),可以参加《Head First Design Patterns》英文版P81,抱歉本人并不知道是否有中文版以及中文版具体页数。
结合现实例子,我们可以这样考虑,像DarkRoast, HouseBlend, Espresso这些,都是特定咖啡种类,而制成一杯饮品,无非是在这些饮品里面添加调味料(Condiments)。那如果我们可以用两个子类来继承Beverage,一个代表饮品“类型”,一个代表“调味料”的基类。当你需要在饮品“添加”调味料时,再不断地添加调味料,价格再去累加,这样的设计更加合理,在这里,调味料就是Decorator,而饮品种类就是Component。这就是Decorator模式。
2. Implementation 实现
那我们来看看具体如何实现这个例子。
首先是基类Beverage:
1 public abstract class Beverage { 2 String description = "Unknown Beverage"; 3 4 public String getDescription() { 5 return description; 6 } 7 8 public abstract double cost(); 9 }
而Component和Decorator都需要继承这个基类,因为他们都具有Description以及Cost。
接下来是各种Component:
例如Espresso以及House Blend:
1 public class Espresso extends Beverage{ 2 public Espresso() { 3 description = "Espresso"; 4 } 5 public double cost() { 6 return 1.99; 7 } 8 }
public class HouseBlend extends Beverage{ public HouseBlend() { description = "House Blend Coffee"; } public double cost() { return .89; } }
CondimentDecorator,负责condiment的抽象类:
1 public abstract class CondimentDecorator extends Beverage{ 2 public abstract String getDescription(); 3 }
接下来继承CondimentDecorator的子类很重要,例如Mocha类,注意里面有一个Beverage的的子类实例去作为代理去delegate,来实现给这个实例添加“调味料”。
1 public class Mocha extends CondimentDecorator{ 2 Beverage beverage; 3 public Mocha(Beverage beverage) { 4 this.beverage = beverage; 5 } 6 7 public String getDescription() { 8 return beverage.getDescription() + ", Mocha"; 9 } 10 11 public double cost() { 12 return beverage.cost()+.20; 13 } 14 }
最后是测试代码:
1 public class test { 2 public static void main(String[] args) { 3 Beverage beverage = new Espresso(); 4 System.out.println(beverage.getDescription()+" $"+beverage.cost()); 5 Beverage beverage2 = new HouseBlend(); 6 System.out.println(beverage2.getDescription()+ " $"+beverage2.cost()); 7 8 //add some condiment 9 beverage2 = new Mocha(beverage2); 10 beverage2 = new Mocha(beverage2); 11 System.out.println(beverage2.getDescription()+" $"+beverage2.cost()); 12 } 13 }
3. Java I/O
其实在Java里,I/O类其实就用了Decorator模式,我们来看看java.io.*的继承关系。
可以看到,FilterInputStream是一个抽象的Decorator类,而FileInputStream, StringBufferInputStream, ByteArrayInputStream则为Component类。
4.Decorator Pattern的“黑暗面” Dark Side
首先,Decorator模式比较难读懂,不够直观。另外,当你创建一个Component实例时,你不单仅仅创建Component实例,你同时需要创建Decorator去修饰(wrap)它。这就造成了编写代码过程的复杂化。但这些问题并不代表Decorator不是一个好的设计模式,设计模式本质上说并无好坏之分,就像根本不存在完美的高内聚低耦合代码,只有不断地尝试去"折中"它。