【JavaIO流】装饰器与适配器模式

装饰器与适配器模式

在Java语言 I/O库的设计中,使用了两个结构模式,即装饰模式和适配器模式。

在任何一种计算机语言中,输入/输出都是一个很重要的部分。与一般的计算机语言相比,Java将输入/输出的功能和使用范畴做了很大的扩充。因此输入输出在Java语言中占有极为重要的位置。

Java语言采用流的机制来实现输入/输出。所谓流,就是数据的有序排列,流可以是从某个源(称为流源,或者 Source of Stream)出来,到某个目的(Sink of Stream)地去。根据流的方向可以将流分成输出流和输入流。程序通过输入流读取数据,想输出流写出数据。

例如:一个Java程序可以使用FileInputStream类从一个磁盘文件读取数据,如下图:

在这里插入图片描述

像FileInputStream这样的处理器叫流处理器。一个流处理器就像一个流的管道一样,从一个流源吸入某种类型的数据,并输出某种类型的数据。上面的示意图叫流的管道图。

类似地,也可以用FileOutputStream类向一个磁盘文件写数据,如下图:

在这里插入图片描述

在实际的应用当中,这样简单的机制并没有太大的用处。程序需要写出的往往是非常结构话的信息,因此这些Byte类型的数据实际上是一些数字、文字、源代码等。Java的I/O库提供了一个称作链接(Chaining)的机制,可以将一个流处理器与另一个流处理器首尾相接,以其中之一的输出为输入,形成一个流管道的链接。

例如,DateInputStream流处理器可以把FileInputStream流对象的输出当做输入,将Byte类型的数据转换成Java的原始数据类型和String数据类型,如下图:

在这里插入图片描述

类似地,向一个文件中写入Byte类型的数据也不是一个简单的过程。一个程序需要向一个文件里写入的数据往往是结构化的,而Byte类型则是原始的类型,因此,在写入的时候必须首先经过转换。DateOutputStream流处理器提供了接受原始数据类型和String数据类型的方法,而这个流处理器的输出数据则是Byte类型。换而言之,DateOutputStream可以将源数据转换成Byte类型的数据,在输出出来。

这样一来,就可以将DateOutputStream与FileOutputStream链接起来。这样做的结果就是,程序可以将原始数据类型和String数据类型的源数据写到这个链接好的双重管道里面,达到将结构话数据写到磁盘文件里的目的,如下图所示:

在这里插入图片描述

这是链接的威力。

流处理器所处理的流必定都有流源,如果将流类所处理的流源分类的话,那么基本可以分成两大类:

  • (1)数组、String、File等,这一种叫原始流源。
  • (2)同样类型的流用做链接流类的流源,就叫做链接流源。

Java I/O库的设计原则

Java语言的I/O库是对各种常见的流源、流汇以及处理过程的抽象化。客户端的Java程序不必知道最终的的流源、流汇是磁盘上的文件还是一个数组,或者是一个线程;也不比插手到诸如数据是否缓存、可否按照行号读取等处理的细节中去。

要理解Java I/O 这个庞大而复杂的库,关键是掌握两个对称性和两个设计模式。

Java I/O库的两个对称性

Java I/O库具有两个对称性,它们分别是:

  • (1)输入-输出对称:比如InputStream 和OutputStream 各自占据Byte流的输入和输出的两个平行的等级结构的根部;而Reader和Writer各自占据Char流的输入和输出的两个平行的等级结构的根部。
  • (2)byte-char对称:InputStream和Reader的子类分别负责byte和插入流的输入;OutputStream和Writer的子类分别负责byte和Char流的输出,它们分别形成平行的等级结构。

Java I/O库的两个设计模式

Java I/O库的总体设计是符合装饰模式和适配器模式的。如前所述,这个库中处理流的类叫流类。

  • 装饰模式:在由InputStream、OutputStream、Reader和Writer代表的等级结构内部,有一些流处理器可以对另一些流处理器起到装饰作用,形成新的、具有改善了的功能的流处理器。
  • 适配器模式:在由InputStream、OutputStream、Reader和Writer代表的等级结构内部,有一些流处理器是对其他类型的流处理器的适配。这就是适配器的应用。
一、装饰模式
1、定义

Decorator装饰器,就是动态地给一个对象添加一些额外的职责,动态扩展,和下面继承(静态扩展)的比较。因此,装饰器模式具有如下的特征:

  • 1、它必须持有一个被装饰的对象(作为成员变量)。
  • 2、它必须拥有与被装饰对象相同的接口(多态调用、扩展需要)。
  • 3、它可以给被装饰对象添加额外的功能。

总结:保持接口,动态增强性能。

装饰器通过包装一个装饰对象来扩展其功能,而又不改变其接口,这实际上是基于对象的适配器模式的一种变种。与对象的适配器模式异同:

  • 1、相同点:都拥有一个目标对象。
  • 2、不同点:适配器模式需要实现旧接口,而装饰器模式必须实现相同接口。

适配器模式是在适配器中,重写旧接口的方法来调用新接口方法,来实现旧接口不改变,同时使用新接口的目的。新接口适配旧接口。

而装饰模式,是装饰器和旧接口实现相同的接口,在调用新接口的方法中,会调用旧接口的方法,并对其进行扩展。

2、意图

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

3、主要解决问题

一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

4、继承与装饰器总结
  • 装饰器模式比继承要灵活。避免了继承体系臃肿。
  • 装饰器模式降低了类于类之间的关系。
  • 你要说用装饰器实现的功能,继承能否实现,我只能说能,但是在代码的结构层次上来说,装饰器模式比继承灵活了很多。
  • 装饰模式与继承关系的目的都是要拓展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  • 装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者“除掉”一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
  • 通过不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出更多不同行为的组合。
二、适配器模式

目的:将一个类的接口转换成客户期望的另一个接口,让原本不兼容的接口可以合作无间。

1、定义

适配器模式是Java I/O库中第二个最重要的设计模式。

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

2、意图

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

3、主要解决问题

主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

4、特点
  • 1、适配器对象实现原有接口。
  • 2、适配器对象组合一个实现新接口的对象。(这个对象也可以不实现一个接口,只是一个单纯的对象)
  • 3、对适配器原有接口方法的调用被委托给新接口的实例的特定方法。(重写旧接口方法来调用新接口功能)

装饰模式的应用

装饰模式在java中的最著名的应用莫过于java I/O标准库的设计了。

由于Java I/O库需要很多性能的各种组合,如果这些性能都是用继承来实现,那么每一种组合都需要一个类,这样就会造成大量行重复的类出现。如果采用装饰模式,那么类的数目就会大大减少,性能的重复也可以减至最少。因此装饰模式是Java I/O库基本模式。装饰模式的引进,造成灵活性和复杂性的提高。因此在使用Java I/O 库时,必须理解Java I/O库是由一些基本的原始流处理器和围绕它们的装饰流处理器所组成的。

InputStream类型中的装饰模式

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

在这里插入图片描述

上图中所有的类都叫做流处理器,这个图叫做(InputStream类型)流处理器图。根据输入流的源的类型,可以将这些流分为两种,即原始流类和链接流处理器。

原始流处理器

原始流处理器接收一个Byte数组对象、String对象、FileDescriptor对象或者不同类型的流源对象(就是前面所说的原始流源),并生成一个InputStream类型的流对象。在InputStream类型的流处理器中,原始流处理器包括以下四种:

  • (1)ByteArrayInputStream:为多线程的通讯提供缓冲区操作工作,接受一个Byte数组作为流的源。
  • (2)FileInputStream:建立一个与文件有关的输入流。接受一个File对象作为流的源。
  • (3)PipedInputStream:可以和PipedOutputStream配合使用,用于读入一个数据管道的数据。接受一个PipedOutputStream作为源。
  • (4)StringBufferInputStream:将一个字符串缓冲区抓换为一个输入流。接受一个String对象作为流的源。

与原始流处理器相对应的是链接流处理器。

链接流处理器

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

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

    • BufferInputStream:用来从硬盘将数据读入到一个内存缓冲区中,并从此缓冲区提供数据。
    • DateInputStream:提供基于多字节的读取方法,可以读取原始数据类型的数据。
    • LineNumberInputStream:提供带有行计算功能的过滤输入流。
    • PushbackInputStream: 提供特殊的功能,可以将已读取的直接“推回”输入流中。
  • (2)ObjectInputStream 可以将使用ObjectInputStream串行化的原始数据类型和对象重新并行化。

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

必须注意的是,虽然PipedInuptStream接受一个流对象PipedOutputStream作为流的源,但是PipedOutputStream流对象的类型不是InputStream,因此PipedInputStream流处理器仍属于原始流处理器。

抽象结构图

在这里插入图片描述

上面流处理器图与装饰模式的结构图有明显的相同之处。实际上InputStream类型的流处理器结构确实符合装饰模式,而这可以从它们在结构中所扮演的角色中分辩出来。

装饰模式的各个角色

在所有InputStream类

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
适配器模式是一种结构型设计模式,它允许将不兼容的对象组合在一起,通过适配器来使其能够正常工作。在Java编程语言中,适配器模式通常用于将现有接口转换为客户端所期望的接口。 下面是一个Java适配器模式的例子,假设我们有一个已经存在的类,该类只能输出字符串,但我们需要输出整数。我们可以使用适配器模式来解决这个问题。 首先,我们定义一个接口,该接口定义了我们期望的输出整数的方法: ``` public interface IntegerOutput { public int outputInt(); } ``` 然后,我们创建一个适配器类,该类实现了这个接口,并将字符串转换为整数输出: ``` public class StringToIntegerAdapter implements IntegerOutput { private String str; public StringToIntegerAdapter(String str) { this.str = str; } public int outputInt() { int intValue = Integer.parseInt(str); return intValue; } } ``` 最后,我们使用适配器类来输出整数: ``` public class Main { public static void main(String[] args) { String str = "123"; IntegerOutput adapter = new StringToIntegerAdapter(str); int intValue = adapter.outputInt(); System.out.println(intValue); } } ``` 在这个例子中,我们使用适配器模式将不兼容的字符串转换为整数输出。通过创建一个适配器类,该类实现了期望的输出整数的接口,并将字符串转换为整数输出。最后,我们使用适配器类来输出整数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值