装饰者模式
1. 定义
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案
2. 构成
- 装饰者和被装饰者有相同的超类
- 可以用一个或多个装饰者包装一个对象
- 在任何需要原始对象(被包装)的场合,可以用装饰过的对象代替它
- 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,已达到特定的目的
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象
Component:抽象组件,每个组件都可以单独使用,或被装饰者包起来使用
ConcreteComponent:具体组件,扩展自Component,使我们要动态地加上新行为的对象
Decorator:抽象装饰者,每个装饰者都“有一个”(包装一个)组件,也就是说,装饰者有一个实例变量以保存某个Component的引用
ConcreteDecorator wrappedObj:记录所装饰的事物(装饰者包着的Component)
3. 示例:饮料
以饮料为主题,在运行时以调料来“装饰”饮料
- Beverage相当于是抽象的Component类
- HouseBlend、DarkRoast、Espresso、Decaf:4个具体组件,每个代表一种咖啡类型
- Milk、Mocha、Soy、Whip:调料装饰者
public abstract class Beverage {
String description = "Unkown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "dark roast coffee";
}
@Override
public double cost() {
return .1;
}
}
public class Decaf extends Beverage {
public Decaf() {
description = "decaf coffee";
}
@Override
public double cost() {
return 1.20;
}
}
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 .89;
}
}
public class Milk extends CondimentDecorator {
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Milk";
}
@Override
public double cost() {
return .10 + beverage.cost();
}
}
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 Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return .10 + beverage.cost();
}
}
4. 具体实例:Java I/O
编写自己的输入装饰者,把输入流内的所有大写字符转成小写
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;
}
}
public class InputTest {
public static void main(String[] args) {
int c;
try {
InputStream in = new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")
)
);
while((c = in.read()) >= 0) {
System.out.print((char)c);
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. 装饰者模式的缺点
- 利用装饰者模式常常造成设计中含有大量的小类
- 在插入装饰者时,必须要小心谨慎
- 采用装饰者实例化组件是,将增加代码的复杂度(贡茶模式、生成器模式)
6. 装饰者模式的优点
- 适合用来建立富有弹性的设计
- 符合开闭原则
- 一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型