1. 装饰器与普通组合有什么区别?
2. 装饰器模式的作用
3. 如何实现装饰器模式?
4. 装饰器在 IO 流中的应用:FilterInputStream、InputStream
1. 装饰器模式的作用。
装饰器模式允许在不改变已有对象的结构下,向已有对象添加新功能。满足开闭原则,也不破坏现有的操作。
装饰器模式的典型应用是 IO 包。
InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);// 附加读写的缓存功能
byte[] data = new byte[128];
while (bin.read(data) != -1) {
//...
}
复制代码
装饰器模式是通过组合来实现附加需求的。而非用继承。原因在于组合爆炸。当 n 个功能和 m 个类组合时,将有 n*m 个新类,将不可维护。
但装饰器模式与简单的组合有两对不同:
①装饰器类和原始类继承同样的父类,这样我们可以对原始类“嵌套”多个装饰器类。
// 既支持缓存读取,又支持按照基本数据类型来读取数据
InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
int data = din.readInt();
复制代码
②装饰器类是对功能的增强。装饰器模式与代理模式代码结构类似,但目的不同,代理类附加的是跟原始类无关的功能,而装饰器类附加的是跟原始类相关的增强功能。
2. 装饰器模式的实现。
// 装饰器模式的代码结构(下面的接口也可以替换成抽象类)
public interface IA {
void f();
}
public class A impelements IA {
public void f() { //... }
}
public class ADecorator impements IA {
private IA a; // 基于组合
public ADecorator(IA a) {
this.a = a;
}
public void f() {
// 功能增强代码
a.f();
// 功能增强代码
}
}
复制代码
3. FilterInputStream
IO 包中,类似 BufferedInputStream、DataInputStream 这样的装饰器类,并不直接继承自 InputStream,而是继承 FilterInputStream。
InputStream 是一个抽象类,大部分方法都有默认实现。按理说,BufferedInputStream 只需要重写需要增强的功能方法即可。但实际上,对于即便是不需要增加缓存功能的方法,BufferedInputStream 还是必须把它重新实现一遍,简单包裹对 InputStream 对象的函数调用。
原因在于,只有具体的 InputStream 类才有真正的 IO 功能,比如 FileInputStream。如果不重写,调用的是 InputStream 的方法,它不具有真正的 IO 功能。装饰器类都有类似的问题,为了避免编写重复的代码,Java IO 抽象出了一个装饰器父类 FilterInputStream。
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {// FilterInputStream将InputStream的方法都做了如下这样类似的包装
return in.read();
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
复制代码
评论