学过装饰模式后,大家会发现,它在Java语言中最著名的应用莫过于Java I/O标准为库的设计了。这一节将以处理Byte流为例,看看装饰模式是怎样得到应用的。

 为什么不用继承而用装饰模式

 我们知道Java I/O库需要很多性能的各种组合,如果说这些性能的组合是通过继承方式来实现的话,那么每一种组合都需要一个类,这样就会出现大量重复性问题的出现,从而使类数目“爆炸”。而如果采用装饰模式,那么不仅类的数目大减少了,性能的重复也可以减至到最少。所以装饰模式是Java I/O库的基本模式。在这里我想再用<<Head First Design Pattern>>中讲到装饰模式时候的一个例子,看看装饰模式是怎么达到不仅类的数目大减少了,性能的重复也可以减至到最少:

 它这个例子大概是说:Beverage是一个抽象类,它被所有在一个咖啡店里卖的饮料继承。Beverage有个抽象方法cost,所有的子类都要实现这个抽象方法,计算它们的价格。现在有四个最基本的咖啡:HouseBlend,DarkRoast,Decaf,Espresso他们都继承自Beverage,现在的需求是说在四个最基本的咖啡里,每个都可以随便地添加调味品,像steamed milk,soy,还有mocha最后是加上whipped milk。如果是说按继承来实现这种几个调味品跟原来咖啡的组合的话,我们会很自然地设计来下面的类图来:

看到了上面的类图了吗,我们不禁会说这就是“类爆炸”。如果是按装饰模式的设计思路我们可以得出下面的设计类图:

我们再来看看Gof里面的标准的装饰模式的类图表示:

仔细看看上面的几个图后我们肯定就会理解这句话了:装饰模式是怎么达到不仅类的数目大减少了,性能的重复也可以减至到最少。

 再回到Java I/O库,由于装饰模式的引用,造成了灵活性和复杂都大大增加了,我们在使用Java I/O库时,必须理解Java I/O库是由一些基本的原始流处理器和围绕它们的装饰流处理器所组成的,这样可以在学习和使用Java I/O库时达到事半功倍的效果。

 下面我用<<Java与模式>>,<<Head First Design Pattern>>或者是网上看到的一些类图来分析:

 首先是InputStream类型中的装饰模式:

 InputStream有七个直接的具体子类,有四个属于FilterInputStream的具体子类,如下图所示:

  上图中所有的类都叫做流处理器,这个图就叫做(InputStream类型的)流处理器图。

  书中提到根据输入流的源的类型,可以将这些流类分成两种,即原始流类(Original Stream)和链接流处理器(Wrapper Stream)。

  原始流处理器

  原始流处理器接收一个Byte数组对象,String对象,FileDiscriptor对象或者不同类型的流源对象,根据上面的图,原始流处理器包括以下四种:

  ByteArrayInputStream:为多线程的通信提供缓冲区操作功能,接收一个Byte数组作为流的源。

  FileInputStream:建立一个与文件有关的输入流。接收一个File对象作为流的源。

  PipedInputStream:可以与PipedOutputStream配合使用,用于读入一个数据管道的数据,接收一个PipedOutputStream作为源。

  StringBufferInputStream:将一个字符串缓冲区转换为一个输入流。接收一个String对象作为流的源。(JDK帮助文档上说明:已过时。此类未能正确地将字符转换为字节。从JDK1.1开始,从字符串创建流的首选方法是通过StringReader类进行创建。只有字符串中每个字符的低八位可以由此类使用。)

  链接流处理器

  所谓链接流处理器,就是可以接收另一个流对象作为源,并对之进行功能扩展的类。InputStream类型的链接处理器包括以下几种,它们都接收另一个InputStream对象作为流源。

  (1)FilterInputStream称为过滤输入流,它将另一个输入流作为流源。这个类的子类包括以下几种:

  BufferedInputStream:用来从硬盘将数据读入到一个内存缓冲区中,并从缓冲区提供数据。

  DataInputStream:提供基于多字节的读取方法,可以读取原始类型的数据。

  LineNumberInputStream:提供带有行计数功能的过滤输入流。

  PushbackInputStream:提供特殊的功能,可以将已经读取的字节“推回”到输入流中。

  (2)ObjectInputStream可以将使用ObjectInputStream串行化的原始数据类型和对象重新并行化。

  (3)SeqcueneInputStream可以将两个已有的输入流连接起来,形成一个输入流,从而将多个输入流排列构成一个输入流序列。

  抽象结构图

  按照上面的这种原始流处理器和链接流处理器的划分,可以用下面的结构图来描述它们之间的关系。

  

 上面的流处理器图跟装饰模式的结构图有着显而易见的相同之处。实际上InputStream类型的流处理器结构确实符合装饰模式。 

 装饰模式结构图

 

  对于上图FilterInputStream查看JDK1.4源代码,部分代码如下:

Public class FilterInputStream extends InputStream {
    /**
     * The input stream to be filtered.
     */
    protected InputStream in;
    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
    //其它代码
}
FilterInputStream继承了InputStream,也引用了InputStream,而它有四个子类,这就是所谓的Decorator模式

  上面这个图向我们传达了这个信息:链接流链接流对象接收一个原始流对象或者另外一个链接流对象作为流源;另一方面他们对流源的内部工作方法做了相应的改变,这种改变是装饰模式所要达到的目的。比如:

  BufferedInputStream“装饰”了InputStream的内部工作方式,使得流的读入操作使用了缓冲机制。在使用了缓冲机制后,不会对每一次的流读入操作都产生一个物理的读盘动作,从而提高了程序的效率,在汲及到物理流的读入时,都应当使用这个装饰流类。

  LineNumberInputStream和PushbackInputStream也同样“装饰”了InputStream的内部工作方式,前者使得程序能够按照行号读入数据;后者能够使程序读入的过程中,退后一个字符。

  DataInputStream子类读入各种不同的原始数据类型以及String类型的数据,这一点可以从它提供的各种read方法看出来,如:readByte(),readInt(),readFloat()等。

  Java语言的I/O库提供了四大等级结构:InputStream,OutputStream,Reader,Writer四个系列的类。InputStream和OutputStream处理8位字节流数据, Reader和Writer处理16位的字符流数据。InputStream和Reader处理输入, OutputStream和Writer处理输出,所以OutputStream,Reader,Writer这三类的装饰模式跟前面详细介绍的InputStream装饰模式大同小异,大家可以看书中其它部分对这三类的详细描述或者从网上也能找到有关资料。为了方便比较这几种类型,顺便附上Java语言的I/O层次结构图:

 下面的图表示:以InputStream和OutputStream形成的层次关系

 下面的图表示:以Reader和Writer形成的层次关系

Java IO 流 设计模式的更多相关文章

  1. 【转】java io 流 设计模式

    知识点:什么是装饰模式: http://wenku.baidu.com/view/ad4eac9f51e79b896802263b.html(原理讲的很清楚) http://wenku.baidu.c ...

  2. Java IO 流 -- 设计模式:装饰设计模式

    在java IO 流中我们经常看到这样的写法: ObjectOutputStream oos = new ObjectOutputStream( new BufferedOutputStream(ne ...

  3. Java IO流题库

    一.    填空题 Java IO流可以分为   节点流   和处理流两大类,其中前者处于IO操作的第一线,所有操作必须通过他们进行. 输入流的唯一目的是提供通往数据的通道,程序可以通过这个通道读取数 ...

  4. JAVA.IO流学习笔记

    一.java.io 的描述 通过数据流.序列化和文件系统提供系统输入和输出.IO流用来处理设备之间的数据传输 二.流 流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数 ...

  5. Java IO流01-总叙

     Java IO包体系结构图: 1.流式部分――IO的主体部分: 2.非流式部分——主要包含一些辅助流式部分的类,如:File类.RandomAccessFile类和FileDescriptor等类: ...

  6. java IO流的继承体系和装饰类应用

    java IO流的设计是基于装饰者模式&适配模式,面对IO流庞大的包装类体系,核心是要抓住其功能所对应的装饰类. 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的 ...

  7. Java:IO流与文件基础

    Java:IO流与文件基础 说明: 本章内容将会持续更新,大家可以关注一下并给我提供建议,谢谢啦. 走进流 什么是流 流:从源到目的地的字节的有序序列. 在Java中,可以从其中读取一个字节序列的对象 ...

  8. java IO流详解

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  9. Java IO流学习总结

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

随机推荐

  1. 使用WMI Filter 实现组策略的筛选!

    今天接到一个客户的一个问题,提到需要分系统版本分发相应的MSI程序.比如简体版接受简体版的分发程序,繁体版接受繁体版的分发程序!这个建立组策略的不同版本分发本身不会太难,我们只需要建立两个不同组策略分 ...

  2. Composer 下载安装类库

    安装 Composer 你需要先下载 composer.phar 可执行文件. curl -sS https://getcomposer.org/installer | php composer.js ...

  3. opendatasource问题

    EXEC sp_configure 'show advanced options', 1 GO RECONFIGURE GO EXEC sp_configure 'Ad Hoc Distributed ...

  4. Linux下测试SSD固态硬盘写入速度

    最近买了一个256GB的SSD固态硬盘,想测试一下写入速度,于是如下操作. 部分代码: gettimeofday(&start, NULL); int fd = open("test ...

  5. [转]Linux 技巧:让进程在后台可靠运行的几种方法

    转自: https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/index.html 我们经常会碰到这样的问题,用 telnet/ssh 登录了远 ...

  6. python技巧:拆分多层嵌套列表

    方法一: >>> import itertools >>> a = [[1, 2], [3, 4], [5, 6]] >>> list(itert ...

  7. 我的第一个python程序——猜数字

    #Author:xiaoxiao age = 22 #标准正确答案 counter = 0 #计数器 for i in range(10): #循环10次 if counter < 3: gue ...

  8. 【转】 [Unity3D]手机3D游戏开发:场景切换与数据存储(PlayerPrefs 类的介绍与使用)

    http://blog.csdn.net/pleasecallmewhy/article/details/8543181 在Unity中的数据存储和iOS中字典的存储基本相同,是通过关键字实现数据存储 ...

  9. [LOJ#2328]「清华集训 2017」避难所

    [LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...

  10. BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】

    题目链接 BZOJ2395 题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积 求权值最小的生成树 题解 如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面 ...