The decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.[1] The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
说到JDK中的装饰者模式,自然想到的是经典示范java.io,以InputStream为例,我们看下装饰者模式如何实现:
InputStream是一个抽象类,包含有如下子类
- ByteArrayInputStream
- FileInputStream
- PipedInputStream
- SequenceInputStream
- FilterInputStream
他们根据不同的应用场景实现父类中以不同方式实现父类中的read方式,但FilterInputStream与别的子类略显不同:
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected FilterInputStream(InputStream in) {
this.in = in;
}
...
}
其维护了一个父类InputStream对象,并且所有的方法都是调用父类方法实现。其实这就是一种装饰者模式:允许向一个现有的对象添加新的功能,同时又不改变其结构。 FilterInputStream通过其子类(以BufferedInputStream为例)向现有对象添加新的功能,同时不改变其结构。其提供了数据缓冲的装饰功能。
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
这里有一个话题:Why is using BufferedInputStream to read a file byte by byte faster than using FileInputStream? : 为什么按字节的方式读取文件BufferedInputStream比FileInputStream要快
这是FileInputStream中read方法源码:
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
其实道理很简单:
FileInputStream调用的是磁盘读取文件字节的本地方法实现,这种开销是非常大的。而BufferedInputStream通过缓存字节的方式(默认8M)减少了与磁盘的交互,自然效率就大大提升。
例如:目标文件大小为32768 bytes。如果使用FileInputStream,32768次的read方法调用背后则是32768次本地调用。而使用BufferedInputStream,仅需4次本地调用而已。当然是在未改变原有FileInputStream总计32768次调用方法read的前提下,增加的缓存数据操作。这就是装饰器的模式
因此,基于FilterInputStream,我们也可以自定义实现一个装饰类,这里我们装饰下read(byte b[], int off, int len)方法
public class CustomFilterInputStream extends FilterInputStream {
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected CustomFilterInputStream(InputStream in) {
super(in);
}
public synchronized int read(byte b[], int off, int len)
throws IOException {
System.out.println("只是加点装饰而已");
return in.read(b, off, len);
}
}
我们看下装饰效果如何:
public class Main {
public static void main(String[] args) throws Exception {
StringBuilder sb = new StringBuilder();
File file = new File("d:\\python.txt");
FileInputStream fis = new FileInputStream(file);
CustomFilterInputStream bis = new CustomFilterInputStream(fis);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
int pos;
byte[] b = new byte[1024];
while ((pos = bis.read(b)) != -1) {
System.out.println(new String(b, 0, pos));
}
fis.close();
bis.close();
stopWatch.stop();
System.out.println("time elapsed: "+stopWatch.getTime());
}
}
运行结果:
只是加点装饰而已
from operator import itemgetter
ord chr
只是加点装饰而已
time elapsed: 1