Head First设计模式:(三)装饰者模式

星巴兹咖啡准备更新订单系统,以合乎他们的饮料供应需求。

他们原先的类设计为:

Head First设计模式:(三)装饰者模式

这样的订单系统没有办法考虑到咖啡调料的部分,把加入不同调料的咖啡看做不同的类会导致类爆炸(每个类的cost方法计算出咖啡加调料的价钱):

Head First设计模式:(三)装饰者模式

很明显,这样的系统难以维护,一旦牛奶的价钱上扬或新增一种焦糖调料,系统将难以改变。

采用实例变量和继承的设计也许能解决一些问题:

Head First设计模式:(三)装饰者模式

Beverage作为一个饮料类,加上实例变量代表是否加入了饮料。

然而当用户想要双倍摩卡咖啡时,这样的系统就显得有些无所适从。

对于冰茶,饮料基类里的有些调料根本不适用,但是也一起继承了过来!

到目前为止,使用继承会造成的问题有:类爆炸,设计死板,以及基类加入的新功能并不适用于所有的子类。

所以继承并不是解决问题的方法,应当使用组合来使系统更有弹性且易于维护。

开放-关闭原则:

设计原则:

类应该对扩展开放,对修改关闭。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。

这个目标需要使用装饰着模式实现:以饮料为主体,然后运行调料来“装饰”饮料。

如图为一个摩卡和奶泡DarkRoast咖啡的设计图:

Head First设计模式:(三)装饰者模式

定义装饰者模式:

装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更具有弹性的替代方案。

装饰者模式类图:

Head First设计模式:(三)装饰者模式

现在让星巴兹咖啡系统也符合装饰者类图:

Head First设计模式:(三)装饰者模式

具体实现:

从饮料下手,将饮料作为一个抽象类:

package com.cafe; public abstract class Beverage { String description = "Unknow Beverage"; public String getDescription() { return description; } public abstract double cost(); }

调料抽象类,也就是装饰者类:

package com.cafe; public abstract class CondimentDecorator extends Beverage{ public abstract String getDescription(); }

实现具体的饮料(浓缩咖啡和综合咖啡):

package com.cafe; public class Espresso extends Beverage { public Espresso() { description = "Espresso"; } public double cost() { return 1.99; } } package com.cafe; public class HouseBlend extends Beverage { public HouseBlend() { description = "HouseBlend"; } public double cost() { return 0.89; } }

实现具体装饰者类(摩卡)

package com.cafe; public class Mocha extends CondimentDecorator{ Beverage beverage; public Mocha(Beverage beverage) { this.beverage=beverage; } public String getDescription() { return beverage.getDescription()+", Mocha"; } public double cost() { return 0.20+beverage.cost(); } }

其他装饰者类的实现方式与摩卡类似。

测试代码:

package com.cafe; public class StartbuzzCoffee { public static void main(String args[]) { Beverage beverage1 = new Espresso(); System.out.println(beverage1.getDescription() + " $" + beverage1.cost()); Beverage beverage2 = new HouseBlend(); beverage2 = new Soy(beverage2); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); } }

测试结果:

Head First设计模式:(三)装饰者模式
JAVA中的装饰者模式(java.io类):

Head First设计模式:(三)装饰者模式

Java I/O引出装饰者模式的一个“缺点”:利用装饰者模式,会造成设计中存在大量的小类。

编写自己的Java I/O装饰者,把输入流中的所有大写字母转成小写:

package com.io; import java.io.*; public class InputTest { public static void main(String[] args) throws IOException { int c; try { InputStream in = new LowerCaseInputStream(new BufferedInputStream( new FileInputStream("D:\\test.txt"))); while ((c = in.read()) >= 0) { System.out.print((char) c); } in.close(); } catch (IOException e) { e.printStackTrace(); } } }

测试程序(测试刚刚写好的I/O装饰者)

package com.io; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class LowerCaseInputStream extends FilterInputStream { protected LowerCaseInputStream(InputStream in) { super(in); // TODO Auto-generated constructor stub } public int read() throws IOException { int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char) c)); } public int read(byte[] b, int offset, int len) throws IOException { int result = super.read(b, offset, len); for (int i = offset; i < offset + result; i++) { b[i] = (byte) Character.toLowerCase((char) b[i]); } return result; } }

测试结果:

Head First设计模式:(三)装饰者模式