设计模式之装饰者模式
情景:
你是一个咖啡店老板,你会做很多种咖啡。
首先有一个超类,饮料类。
咖啡可以加很多种调料,就假设是A,B,C……
那么你会有AB型的,AC型,BC型,A型的,B型,C型,ABC型的咖啡,那么你需要有六种具体子类。
如果有n种调料,你就要有(2^n-1)种具体子类有木有啊!!!
同时,如果,你添加了一种调料或者改变一种调料!!!会死人呐有木有!!!
怎么办?? 可以考虑使用组合(compisition)和委托(delegation)。
设计原则:类应该对扩展开放,对修改关闭。
装饰者模式
以饮料为主体,然后在运行时,以调料来装饰(decorate)饮料。
设饮料价格是X,加了调料A,可以用A把X包装起来A(X),又加了调料B,再用B把A(x)包装起来,B(A(X))。
- 装饰着和被装饰者具有相同的超类型。
- 你可以用一个或多个装饰者包装一个对象。
- 既然装饰者和被装饰者对象具有相同的超类型,所以在任何需要原始对象(被包装的)的场合,都可以用装饰过的对象代替它。
- 装饰者可以在所委托被装饰者的行为之前/或之后,加上自己的行为,已达到特定的目的。
- 对象可以在任何时候被装饰,可以在运行时动态地,不限量地用你喜欢的装饰者来装饰对象。
装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
类图:
解决最开始的咖啡问题~~
首先饮料类:
public abstract class Beverage { String description = "Unknown Beverage"; public String getDescription() { return description; } public abstract double cost(); }
然后是两种具体的饮料
public class Espresso extends Beverage { public Espresso() { description = "Espresso"; } @Override public double cost() { return 1.99; } }
public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } @Override public double cost() { return 0.89; } }
调料抽象类:
public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); }
具体调料类(包装类):
摩卡
public class Mocha extends CondimentDecorator { Beverage beverage; // 被装饰者 public Mocha(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ", Mocha"; } @Override public double cost() { return .20 + beverage.cost(); } }
豆浆
public class Soy extends CondimentDecorator { Beverage beverage; // 被装饰者 public Soy(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ", Soy"; } @Override public double cost() { return .15 + beverage.cost(); } }
好了,测试一下:
public class StarBuzzCoffee { public static void main(String[] args) { Beverage beverage = new Espresso(); // 一杯Espresso 不加调料 System.out.println(beverage.getDescription() + " $" + beverage.cost()); Beverage beverage2 = new HouseBlend(); // 一杯HouseBlend 加摩卡 加豆浆 beverage2 = new Mocha(beverage2); beverage2 = new Soy(beverage2); System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); } }
输出:
Espresso $1.99
House Blend Coffee, Mocha, Soy $1.24
Java的io包中使用了装饰者模式。
编写自己的Java IO装饰者
import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream in) { super(in); } 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; } }
测试一下:(使用了try-with-resources)
public class InputTest { public static void main(String[] args) { int c; try ( InputStream in = new LowerCaseInputStream( new LowerCaseInputStream( new FileInputStream("test.txt"))); ) { while ((c = in.read()) > 0) { System.out.print((char)c); } } catch (IOException e) { e.printStackTrace(); } } }
test.txt内容: I know the Decorator Pattern therefore I RULE!
输出: i know the decorator pattern therefore i rule!
装饰者模式的缺点:
会产生大量的类。实例化的时候会很复杂。