Decorator 模式属于结构型模式。
1.意图
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式比生成子类更为灵活。
2.别名
包装器Wrapper
3.动机
有时候我们希望给某个对象而不是某个类添加一些功能。使用继承机制是添加功能的一种有效途径,但是不够灵活。一种叫较为灵活的方式是将组件嵌入另外一个对象中,由这个对象添加新功能。我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明,它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作。
透明性使得你可以递归的嵌套多个装饰,从而可以添加任意多的功能。
4.适用性
l 在不影响其他的对象的情况下,以动态透明的方式给单个对象添加职责
l 处理那些可以撤销的职责
l 当不能采用子类的方法进行扩充时。一种是有可能有大量独立的扩展,使得子类数目爆炸性增长。另外一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类
5.结构
6.效果
² 优点
n 比静态继承更灵活。可以使用添加和分离的方法,用装饰在运行时刻增加和删除职责
n 避免在层次结构高层的类有太多特征。Decorator模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可以预见的特征
² 缺点
n Decorator与它的Component不一样。Decorator是一个透明的包装类,如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有区别的,因此使用装饰时不应该依赖对象标识
n 有很多小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是他们的类或者是他们的属性值有所不同。
7.实现
(1) 接口的一致性:装饰对象的接口必须与它所装饰的Component的接口是一致的,所有的Concrete Decorator都必须有一个公共的父类
(2) 省略抽象的Decorator类。当你仅仅需要添加一个职责时候,没有必要定义抽象的Decorator类,你常常需要处理现存的类层次结构而不是设计一个新系统,这是你可以把Decoratord向Component转发请求的职责合并到ConcreteDecorator中
(3) 保持Component类的简单性,它应集中于定义接口而不是存储数据
(4) 改变对象外壳和改变对象内核:当Component类本来就很庞大时候,使用Decorator模式的代价太高,Strategy模式相对更好一些,在Strategy模式中,组件将它的一些行为转发给一个独立的策略对象,我们可以替换Strategy对象,从而改变或者扩充组件的功能(基于Strategy的方法可能需要修改component组件以适应新的扩充,另外一方面,一个策略可以有自己特定的接口,而装饰的接口必须与组件接口一致
8.相关模式
Adapter模式:Decorator 模式 不同于Adapter模式,因为装饰仅仅改变了对象的职责而不改变它的接口,而适配器将给对象一个全新的接口
Composite模式:可以将装饰视为一个退化的,仅有一个组件的组合,然而装饰仅仅给对象添加一些额外的职责—他的目的不在对象聚集
Strategy模式:用一个装饰可以改变对象的外表,用Strategty模式可以使你改变对象的内核
9.应用实例—Java I/O包中的Decorator模式
(1) OutputStream是一个抽象类,它是所有输出流的公共父类,其源代码如下:
public abstract class OutputStream implements Closeable, Flushable {
public abstract void write(int b) throws IOException;
...
}
它定义了write(int b)的抽象方法。这相当于Decorator模式中的Component类。
(2) 以ByteArrayOutputStream为例:
public class ByteArrayOutputStream extends OutputStream {
protected byte buf[];
protected int count;
public ByteArrayOutputStream() {
this(32);
}
public ByteArrayOutputStream(int size) {
if (size 〈 0) {
throw new IllegalArgumentException("Negative initial size: " + size);
}
buf = new byte[size];
}
public synchronized void write(int b) {
int newcount = count + 1;
if (newcount 〉 buf.length) {
byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
System.arraycopy(buf, 0, newbuf, 0, count);
buf = newbuf;
}
buf[count] = (byte)b;
count = newcount;
}
...
}
它实现了OutputStream中的write(int b)方法,因此我们可以用来创建输出流的对象,并完成特定格式的输出。它相当于Decorator模式中的ConcreteComponent类。
(3) FilterOutputStream,代码如下:
public class FilterOutputStream extends OutputStream {
protected OutputStream out;
public FilterOutputStream(OutputStream out) {
this.out = out;
}
public void write(int b) throws IOException {
out.write(b);
}
...
}
同样,它也是从OutputStream继续。但是,它的构造函数需要传递一个OutputStream的引用给它,并且它将保存对此对象的引用。这个FilterOutputStream类相当于Decorator模式中的Decorator类,它的write(int b)方法只是简单的调用了传入的流的write(int b)方法,而没有做更多的处理,因此它本质上没有对流进行装饰,所以继续它的子类必须覆盖此方法,以达到装饰的目的。
(4) BufferedOutputStream 和 DataOutputStream是FilterOutputStream的两个子类,它们相当于Decorator模式中的ConcreteDecorator,并对传入的输出流做了不同的装饰。以BufferedOutputStream类为例:
public class BufferedOutputStream extends FilterOutputStream {
...
private void flushBuffer() throws IOException {
if (count 〉 0) {
out.write(buf, 0, count);
count = 0;
}
}
public synchronized void write(int b) throws IOException {
if (count 〉= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
...
}
这个类提供了一个缓存机制,等到缓存的容量达到一定的字节数时才写入输出流。首先它继续了FilterOutputStream,并且覆盖了父类的write(int b)方法,在调用输出流写出数据前都会检查缓存是否已满,假如未满,则不写。这样就实现了对输出流对象动态的添加新功能的目的。