java io 中的装饰者模式
装饰者模式可以对原本方法进行增强,例如 java io 中的 FileInputStream 没有缓存功能,执行速度慢,利用 BufferedInputStream 对 FileInputStream 进行装饰就能实现有缓存功能的 io 流方法,按照这个思路,我们手动写一个 BufferedInputStream
public class BufferedInputStream extends FileInputStream {
byte[] buffer = new byte[8192];
@Override
public int read(){
// 调用父类的read方法,添加缓存的逻辑
}
}
装饰者模式有两种编程方式,继承和组合,在 java io 中使用的是不使用继承的方式,这是因为 InputStream 的实现类实在是有很多,如果一一继承的话会导致类的继承结构变得非常复杂。如果使用组合的方式的话,也会有很多麻烦,如下所示
public class BufferedInputStream {
private InputStream inputStream;
public BufferedInputStream(InputStream inputStream){
this.inputStream = inputStream;
}
byte[] buffer = new byte[8192];
public int read(){
// 调用父类的read方法,添加缓存的逻辑
}
// 其他不需要进行增强的方法
public void other(){
inputStream.other();
}
}
如果用组合的方式,其他不需要增强的方法也要全部重写实现,在方法体中调用组合的方法来返回,代码的重复性很高,因此,它使用了一种很巧妙的方法,就是 组合 + 继承
既然使用继承,那继承的 FileInputStream 肯定不行,继承的坏处依旧没有解决呀,如果继承 InputStream 的话,组合的坏处依旧没有解决,仍需全部重写并调用组合类中的方法。查看 jdk 源码,发现它继承的是 FilterInputStream,那它是如何解决这个问题的呢,我们来看源代码
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
public long skip(long n) throws IOException {
return in.skip(n);
}
// 其他方法类似,都是调用组合类中的方法
}
这个设计就非常巧妙,把组合类放到 FilterInputStream 中, FilterInputStream 中全部重新实现类组合类中的方法,然后 BufferedInputStream 再继承它,这样 BufferedInputSteam 只要对需要缓存的方法进行增强就行了,不用关心其他不需要增强的方法。
java io 中不只有对缓存的增强,还有 ZipInputStream 压缩流、 DataInputStream 数据流等,这些都继承了 FilterInputStream`,这样这些类只需关心自己需要进行增强的方法即可
装饰者模式比代理模式特殊的地方
如果把装饰者模式和静态代理进行比较,会发现它们都是对原始方法的增强,那装饰者模式有什么特殊的地方呢
1、代理模式中,代理类增加的是与原始方法无关的内容,而装饰者模式是对原始方法相关功能的增强
2、装饰者模式包装类都继承了同一个对象 FilterInputStream 或 FilterOutputStream ,这两个类又继承了 InputStream 或 OutputStream,因此它们能进行嵌套,如下所示
FileInputStream in = new FileInputStream("D://xxx.xml");
BufferedInputStream buf = new BufferedInputStream(in);
ZipInputStream zip = new ZipInputStream(buf);