JDK中的设计模式之装饰者模式

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值