IO的装饰模式

我已经在维基百科中读过Decorator模式用于.Net和Java IO类。

有人可以解释如何使用? 和一个可能的例子,它的好处是什么?

在wiki上有一个窗口窗体的例子,但我想知道它是如何发生在IO类。

InputStream是一个抽象类。 像BufferedInputStreamGzipInputStreamObjectInputStream等大多数具体实现都有一个构造函数,它需要一个相同抽象类的实例。 这是装饰器模式的识别关键(这也适用于采取相同接口的实例的构造函数)。

当使用这样的构造函数时,所有方法都将委托给包装的实例,同时改变方法的行为。 例如,预先在内存中缓冲stream,预先解压缩stream或以不同的方式解释stream。 有些甚至还有其他的方法,最终还将进一步委托给包装的实例。 这些方法用额外的行为来装饰包装的实例。

假设我们在Gzip文件中有一堆序列化的Java对象,并且我们希望快速读取它们。

首先打开它的inputstream:

 FileInputStream fis = new FileInputStream("/objects.gz"); 

我们想要速度,所以让我们把它缓冲在内存中:

 BufferedInputStream bis = new BufferedInputStream(fis); 

该文件是gzipped,所以我们需要解压缩它:

 GzipInputStream gis = new GzipInputStream(bis); 

我们需要反序列化这些Java对象:

 ObjectInputStream ois = new ObjectInputStream(gis); 

现在我们终于可以使用它了:

 SomeObject someObject = (SomeObject) ois.readObject(); // ... 

好处是你有很大的自由来装饰stream使用一个或多个各种装饰,以满足您的需求。 比单独使用ObjectGzipBufferedFileInputStreamObjectBufferedFileInputStreamGzipBufferedFileInputStreamObjectGzipFileInputStreamObjectFileInputStreamGzipFileInputStreamBufferedFileInputStream等组合类的方法要好得多。

请注意,当你要closuresstream时,只closures最外层的装饰器就足够了。 它会将closures呼叫一直委托给底部。

 ois.close(); 

也可以看看:

  • Java核心库中的GoFdevise模式示例

让我们在通过Java IO类之前了解Decorator模式的组件。

在这里输入图像说明

装饰模式有四个组成部分

  1. Component: Component定义了可以dynamic添加职责的对象的接口
  2. ConcreteComponent:它只是一个Component接口的实现
  3. Decorator: Decorator有一个Component的引用,也符合Component接口。 Decorator者本质上是包装Component
  4. ConcreteDecorator: ConcreteDecorator只是增加了原始Component职责。

装饰器模式可以被用来静态地或者在运行时扩展(修饰)某个对象的function,而不依赖于同一类的其他实例,只要在devise时做一些基础工作即可。 这是通过devise一个包装original class的新的Decorator类来实现的。

现在让我们将这些概念映射到java.io派生类。

零件:

InputStream :

这个抽象类是表示input字节stream的所有类的超类。

需要定义InputStream子类的应用程序必须始终提供一个返回下一个input字节的方法。

public abstract int read()是一个抽象方法。

ConcreteComponent:

FileInputStream :

FileInputStream从文件系统中的文件获取input字节。 哪些文件可用取决于主机环境。

FileInputStream用于读取诸如图像数据之类的原始字节stream。 为了读取字符stream,请考虑使用FileReader。

InputStream的所有ConcreteComponents的例子:

 AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream 

装饰:

FilterInputStream :

FilterInputStream包含一些其他的inputstream,它用作其基本的数据源,可能会沿途转换数据或提供附加function。

请注意, FilterInputStream实现了InputStream => Decorator implements Component as shown in UML diagram

 public class FilterInputStream extends InputStream 

ConcreteDecorator:

的BufferedInputStream

BufferedInputStream将function添加到另一个inputstream,即缓冲input和支持标记和重置方法的function。

所有ConcreteDecorators例子:

 BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, DeflaterInputStream, DigestInputStream, InflaterInputStream, LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream 

工作示例代码:

我已经使用BufferedInputStream来读取一个单词的每个字符,它已经存储在一个文本文件a.txt中

 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt"))); while(bis.available()>0) { char c = (char)bis.read(); System.out.println("Char: "+c);; } 

何时使用这种模式:

  1. 对象的职责和行为应该dynamic添加/删除
  2. 具体实施应该与责任和行为分离
  3. 当分类分类过于昂贵时,无法dynamic增加/删除责任

在.NET中,有一堆stream装饰器,如BufferedStream,CryptoStream,GzipStream等。所有这些装饰Stream类。

当您操作input/输出stream时,装饰器模式在java.io类中使用(读者和作者同样适用)。

inputstream,bytearrayinputstream,stringbuilderinputstreams等是基于元素。 Filterinputstream是装饰类的基类。 filterinputstream(例如bufferedinputstream)在读取stream或写入stream时可以做其他事情。

它们是通过封装stream来构build的,并且是stream本身。

 new BufferedReader( new FileInputStream() ).readLine(); 

我想不出任何在java.net中实现这个模式的类,但是我想你会被告知这个包,因为它与java.io紧密相关(比如socket.getInputStream)。

实际上, 这里是O'Relly的一个课程,它解释了如何在java.io中实现装饰器。

问候,Stéphane

您可以修饰input/输出stream的一种方法是对其进行压缩/解压缩。 例如,查看java.util.zip的类。 这样的装饰stream可以与“常规”input/输出stream完全相同的方式使用,压缩/解压缩完全透明地执行。

装饰器模式用于为现有对象(如库中定义的类)添加function。 你可以“装饰”它来适应你的需求。 如果您有兴趣了解更多关于模式的信息,我推荐四人帮的“devise模式”。

那么我可能会迟到,但这个问题不会老化。 理解装饰器的关键在于,它使您能够将对象与现有对象连接到另一个现有对象,等等。 在构造函数中实现这个模式是很受欢迎的。 例如,

  Icecream ic = new RainbowTopUp(new ChocoTopUp(new Vanilla())); 

如果你看看维基百科的图表,你会看到ConcreteComponentDecoratorinheritance自同一个超类/接口Component 。 也就是说,这两个类具有相同的实现方法。

但是,在Decorator类中,您会看到一个箭头回到Component ,这意味着您在Decorator类的某处使用Component 。 在这种情况下,您可以使用Component作为Decorator中构造函数的数据types。 这是一个很大的窍门。 没有这个技巧,你将无法将一个新的对象插入到一个现有的对象中。

之后,您可以创build从Decorator类inheritance的子类。 因为所有的类都有相同的根,所以每一个类都可以自由的插入而不需要任何的顺序。