设计模式系列装饰者模式

趁面试之余,整理了一些常用的设计模式,针对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中还有许多其它设计模式的体现,比如说监听者模式等等。

小杨 wechat
这可以加我为好友!

热评文章