趁面试之余,整理了一些常用的设计模式,针对java实现的的,好增加一些自信。
装饰者模式:动态地将责任附加到对象上,如要扩展功能,装饰者比继承提供了更有弹性的替代方案
一,应用场景
解决咖啡店饮料搭配和计算问题,java的java io或者java nio,也使用装饰者行为设计模式。画一个简单的类图,有助于理解。
需要注意的是,装饰者和被装饰对象有相同的超类型,他们都继承共同的抽象类。
以InputStream为例,它是一个抽象类:
public abstract class InputStream implements Closeable {
…
…
}
并定义有抽象方法
public abstract int read() throws IOException;
该抽象方法由具体的子类去实现,通过InputStream的族谱图可以看到,直接继承了InputStream,并且提供某一特定功能的子类有:
ByteArrayInputStream
FileInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
StringBufferInputStream
这些子类都具有特定的功能,比如说,FileInputStream代表一个文件输入流并提供读取文件内容的功能,ObjectInputStream提供了对象反序列化的功能。
InputStream这个抽象类有一个子类与上述其它子类非常不同,这个子类就是FilterInputStream,可参见上图中的InputStream族谱图。
翻开FilterInputStream的代码,我们可以看到,它内部又维护了一个InputStream的成员对象,并且它的所有方法,都是调用这个成员对象的同名方法。换句话说,FilterInputStream它什么事都不做。就是把调用委托给内部的InputStream成员对象。如下所示:
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);
}
public int available() throws IOException {
return in.available();
}
public void close() throws IOException {
in.close();
}
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
public synchronized void reset() throws IOException {
in.reset();
}
public boolean markSupported() {
return in.markSupported();
}
FilterInputStream的又有其子类,分别是:
BufferedInputStream
DataInputStream
LineNumberInputStream
PushbackInputStream
虽然从上面代码看FilterInputStream并没有做什么有卵用的事,但是它的子类可不同了,以BufferedInputStream为例,这个类提供了提前读取数据的功能,也就是缓冲的功能。可以看看它的read方法:
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
可以看到,当pos>=count时,意即需要提前缓冲一些数据的时候到了,那么就会调用fill()将缓冲区加满,以便后续读取。由于本文只讨论io流的装饰器模式,所以关于具体实现细节将不会展开讨论,比如本文不会讨论fill()方法是如何实现的,在这里可以先将它当做一个黑盒子。
从这里可以开始感受到,BufferedInputStream就是一个装饰者,它能为一个原本没有缓冲功能的InputStream添加上缓冲的功能。
比如我们常用的FileInputStream,它并没有缓冲功能,我们每次调用read,都会向操作系统发起调用索要数据。假如我们通过BufferedInputStream来装饰它,那么每次调用read,会预先向操作系统多拿一些数据,这样就不知不觉中提高了程序的性能。如以下代码所示:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(“/home/user/abc.txt”)));
同理,对于其它的FilterInputStream的子类,其作用也是一样的,那就是装饰一个InputStream,为它添加它原本不具有的功能。OutputStream以及家属对于装饰器模式的体现,也以此类推。
JDK中的io流的设计是设计模式中装饰器模式的一个经典示范,如果细心发现,JDK中还有许多其它设计模式的体现,比如说监听者模式等等。