java 用于IO的GoF装饰器模式的用例和示例

dba5bblo  于 2023-04-04  发布在  Java
关注(0)|答案(8)|浏览(174)

我在wikipedia中读到 Decorator pattern 用于 .NetJava IO 类。
有谁能解释一下这是如何被使用的吗?用一个可能的例子来说明它的好处是什么?
在wikipedia上有一个 *Windows窗体 * 的例子,但我想知道它是如何与 Java IO 类一起发生的。

bakd9h0s

bakd9h0s1#

InputStream是一个抽象类。大多数具体实现,如BufferedInputStreamGzipInputStreamObjectInputStream等,都有一个构造函数,它接受相同抽象类的示例。这是装饰器模式的识别关键(这也适用于接受相同接口示例的构造函数)。
当使用这样的构造函数时,所有方法都将委托给 Package 的示例,并改变方法的行为方式。例如,预先在内存中缓冲流,预先解压缩流或以不同的方式解释流。有些甚至有额外的方法,最终也会进一步委托给 Package 的示例。这些方法用额外的行为装饰 Package 的示例。
假设我们在一个Gzip文件中有一堆序列化的Java对象,并且我们希望快速读取它们。
首先打开它的一个inputstream:

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

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

BufferedInputStream bis = new BufferedInputStream(fis);

文件是gzip的,所以我们需要ungzip它:

GzipInputStream gis = new GzipInputStream(bis);

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

ObjectInputStream ois = new ObjectInputStream(gis);

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

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

这样做的好处是,你可以自由地使用一个或多个不同的装饰器来装饰流,以满足你的需要。这比为每个可能的组合(如ObjectGzipBufferedFileInputStreamObjectBufferedFileInputStreamGzipBufferedFileInputStreamObjectGzipFileInputStreamObjectFileInputStreamGzipFileInputStreamBufferedFileInputStream等)使用一个类要好得多。
请注意,当你要关闭流时,只关闭 outermost 装饰器就足够了。它会将close调用一直委托到底部。

ois.close();

另请参见:

yduiuuwa

yduiuuwa2#

在学习java IO类之前,让我们先了解一下Decorator模式的组件。
Decorator模式有四个组成部分(维基百科)
1.* 组件:* 组件 * 为可以动态添加责任的对象定义接口
1.ConcreteComponent: 它只是 Component 接口的一个实现
1.*Decorator:Decorator 有一个 Component 的引用,也符合 Component 接口,Decorator本质上是 Package Component
1.*ConcreteDecorator:ConcreteDecorator 只是将职责添加到原始的 Component
现在让我们将这些概念Map到java.iopakge类。

  • 组件:*

InputStream
这个抽象类是表示输入字节流的所有类的超类。
需要定义InputStream子类的应用程序必须始终提供返回输入的下一个字节的方法。
public abstract int read()是一个抽象方法。

  • 混凝土构件:*

FileInputStream
FileInputStream从文件系统中的文件获取输入字节。哪些文件可用取决于主机环境。
FileInputStream用于阅读原始字节流,如图像数据。对于读取字符流,请考虑使用FileReader。
InputStream的所有ConcreteComponents示例:

AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, 
InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, 
StringBufferInputStream
  • 装饰师:*

FilterInputStream
FilterInputStream包含一些其他输入流,它将其用作其基本数据源,可能会在此过程中转换数据或提供其他功能。
请注意FilterInputStream实现了InputStream =〉*Decorator实现了UML图中所示的组件 *。

public class FilterInputStream
extends InputStream
  • ConcreteDecorator:*

BufferedInputStream
BufferedInputStream向另一个输入流添加功能-即缓冲输入以及支持mark和reset方法的能力。
所有 ConcreteDecorators 的示例:

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

工作示例代码:

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

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

何时使用此模式:

1.应该动态地添加/删除对象职责和行为
1.具体的实现应该与责任和行为脱钩
1.当动态添加/删除职责时,子类化的成本太高

byqmnocz

byqmnocz3#

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

zvms9eto

zvms9eto4#

A -装饰器模式

A.1 -装饰器模式用例

装饰器模式用于在不改变遗留类的情况下扩展遗留功能。例如,我们有一个实现接口的具体类。但是我们需要扩展现有方法的功能,因为现有类及其方法已经被其他类使用,因此我们不想在现有类中进行更改。但是我们也需要在新类上扩展功能。那么我们怎么解决这个问题呢?

1- We can't change the existing legacy code
2- We want to extend the functionality

所以我们使用装饰器模式,将现有的类 Package 在装饰器中。

B -基础GoF装饰模式示例

这里我们有一个简单的接口和一个implementation/concrete类。接口有一个简单的方法,它是getMessageOfTheDay,它返回一个String。假设有很多其他的类使用这个方法。所以如果我们想在implementation/concrete类中做一个改变,它会影响旧的遗留代码。我们只想为新的类改变它,所以我们使用装饰器模式。
下面是一个Gang Of Four Decorator Design模式的小例子;

B.1 -Greeter.java

public interface Greeter {
    String getMessageOfTheDay();
}

B.2 -BasicGreeter.java

public class BasicGreeter implements Greeter {

    @Override
    public String getMessageOfTheDay() {
        return "Welcome to my server";
    }

}

B.3 -抽象装饰类:GreeterDecorator.java

public abstract class GreeterDecorator implements Greeter {

    protected Greeter greeter;

    public GreeterDecorator(Greeter greeter) {
        this.greeter = greeter;
    }

    public String getMessageOfTheDay() {
        return greeter.getMessageOfTheDay();
    }

}

B.4 -混凝土装饰类:StrangerDecorator.java

public class StrangerDecorator extends GreeterDecorator {

    public StrangerDecorator(Greeter greeter) {
        super(greeter);
    }

    @Override
    public String getMessageOfTheDay() {
        return "Hello Stranger " + super.getMessageOfTheDay();
    }

}

B.5 - Demo代码:java

public class DecoratorDemo {

    public static void main(String[] args) {
        Greeter greeter = new BasicGreeter();

        String motd = greeter.getMessageOfTheDay();

        System.out.println(motd);

        Greeter newGreeter = new StrangerDecorator(greeter);

        String newMotd = newGreeter.getMessageOfTheDay();

        System.out.println(newMotd);

        Greeter muchNewGreeter = new StrangerDecorator(new StrangerDecorator(greeter));

        String newestMotd = muchNewGreeter.getMessageOfTheDay();

        System.out.println(newestMotd);
    }

}

看一下这些例子。需要抽象装饰器类来 Package 原始合约和实现。使用抽象装饰器,您可以创建新的多个装饰器,但在这个例子中,BasicGreeter被 Package 在抽象装饰器中,我们只创建了新的装饰器类StrangeGreeter。请注意装饰器类可以像火车一样使用。我们可以将一个装饰器 Package 在另一个装饰器中,或者 Package 在同一个装饰器中。2功能是可扩展的,但是原始的类被保留而没有任何修改。

C - OutputStream Demo

让我们来看看这个例子。我们想用OutputStream写一个字符串到file。下面是演示代码;

C.1 -OutputStream写文件示例

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileWriterDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./normal.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        String content = "I love Commodore 64";

        oStream.write(content.getBytes());

        oStream.close();
    }

}

C.2 - JSON装饰器输出:normal.txt

将在项目文件夹下创建一个名为“normal.txt”的新文件,内容为:

I love Commodore 64

D - JSON OutputStream Decorator Demo

现在,我想创建一个JSON Package 器格式,如下所示;

{
    data: <data here>
}

我想要的是把内容写在一个简单的一个字段JSON格式里面,怎么样才能达到这个目的呢?有很多琐碎的方法,不过我会使用 *GoF装饰器模式 *,写一个JSONDecorator,它扩展了Java的OutputStream类;

D.1 -输出流的JSON装饰器:JSONStream.java

public class JSONStream extends OutputStream {

    protected OutputStream outputStream;

    public JSONStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void write(int b) throws IOException {
        outputStream.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        String content = new String(b);

        content = "{\r\n\tdata:\"" + content + "\"\r\n}";

        outputStream.write(content.getBytes());
    }

}

D.2 - JSON Decorator演示:JSONDecoratorDemo.java

public class JSONDecoratorDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./json.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        JSONStream js = new JSONStream(oStream);

        String content = "I love Commodore 64";

        js.write(content.getBytes());

        js.close();
        oStream.close();
    }

}

D.3 - JSON装饰器输出:json.txt

{
    data:"I love Commodore 64"
}

实际上,OutputStream本身就是一个装饰器模式,它是抽象的装饰器,这里的具体装饰器是JSONStream类。

mrphzbgm

mrphzbgm5#

当java.io您操作输入/输出流时,在www.example.com类中使用装饰器模式(同样适用于读取器和写入器)。
inputstream、bytearrayinputstream、stringbuilderinputstreams等都是based元素。Filterinputstream是decorator类的基类。过滤器输入流(如bufferedinputstream)可以在读取或写入流时做其他事情。
它们是通过封装流构建的,本身就是流。

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

我想不出在www.example.com中有任何类实现了这种模式java.net,但我想你已经被告知这个包,因为它与www.example.com紧密相连java.io(例如socket.getInputStream)。
实际上,这里有一个来自O 'Relly(pdf on uwosh.edu | archive.orgslides on slideshare.net)的课程,解释了如何在java.io中实现装饰器。

1szpjjfi

1szpjjfi6#

修饰输入/输出流的一种方法是对其应用压缩/解压缩。例如,参见java.util.zip中的类。这样的修饰流可以与“常规”输入/输出流完全相同的方式使用,压缩/解压缩完全透明地执行。

r9f1avp5

r9f1avp57#

装饰器模式用于向现有对象(例如在库中定义的类)添加功能。然后您可以“装饰”它以满足您的需求。如果您有兴趣了解更多关于模式的信息,我推荐Gang of Four的“设计模式”。

p1tboqfb

p1tboqfb8#

好吧,我可能迟到了,但这个问题永远不会过时。理解Decorator的关键点是,它让你能够将一个对象插入到一个现有对象到另一个现有对象等等。在构造函数中实现这种模式很流行。例如,

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

如果你查看维基百科中的图表,你会看到 ConcreteComponentDecorator 继承自同一个超类/接口 Component。也就是说,这两个类具有相同的实现方法。
然而,在 Decorator 类中,你会看到一个指向 Component 的箭头,这意味着你在 Decorator 类中的某个地方使用了 Component。在这种情况下,你在 Decorator 中使用了 Component 作为构造函数的数据类型。这是一个大技巧。没有这个技巧,你将无法将新对象插入到现有对象中。
之后,您可以创建继承 Decorator 类的子类。因为所有类都有相同的根,所以每个类都可以自由插入,没有任何顺序。

相关问题