IT-Swarm.Net

用例和GoF装饰模式的例子 IO

我读过 维基百科 那个 装饰模式 用于 .Net Java IO classes。.

任何人都能解释一下这是如何使用的吗?它有一个可能的例子,有什么好处?

在维基百科上有一个 Windows窗体 的例子但是我想知道它是如何发生的 Java IO classes。.

51
DarthVader

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

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

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

首先打开它的输入流:

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

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

BufferedInputStream bis = new BufferedInputStream(fis);

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

GzipInputStream gis = new GzipInputStream(bis);

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

ObjectInputStream ois = new ObjectInputStream(gis);

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

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

好处是您可以使用一个或多个装饰器来自由地装饰流以满足您的需求。这比为每个可能的组合设置一个类要好得多,例如ObjectGzipBufferedFileInputStreamObjectBufferedFileInputStreamGzipBufferedFileInputStreamObjectGzipFileInputStreamObjectFileInputStreamGzipFileInputStreamBufferedFileInputStream等。.

请注意,当您即将关闭流时,只需关闭最外层装饰器即可。它会将关闭呼叫一直委托给底部。.

ois.close();

也可以看看:

127
BalusC

快速,可靠且价格合理的云托管

注册并在30天内获得$50奖金!

在通过Java IO类之前,让我们理解 Decorator pattern的组件。.

enter image description here 

装饰器 pattern有四个组件

  1. Component:Component定义可以动态添加任务的对象的接口
  2. ConcreteComponent:它只是Component接口的一个实现
  3. Decorator:Decorator引用Component,并且符合Component接口。装饰器本质上是包装Component
  4. ConcreteDecorator:ConcreteDecorator只是将责任添加到原始Component。.

只要在设计时完成一些基础工作,装饰器模式可用于静态地(或在某些情况下)在运行时扩展(装饰)某个对象的功能,而与同一类的其他实例无关。这是通过设计一个包装原始类的新Decorator类来实现的。.

现在让我们将这些概念映射到Java.io pacakge类。.

零件:

InputStream

此抽象类是表示输入字节流的所有类的超类。.

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

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

ConcreteComponent:

FileInputStream

FileInputStream从文件系统中的文件获取输入字节。可用的文件取决于主机环境。.

FileInputStream用于读取原始字节流,例如图像数据。要读取字符流,请考虑使用FileReader。.

InputStream的所有ConcreteComponents的示例:

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

装饰者:

FilterInputStream

FilterInputStream包含一些其他输入流,它将其用作其基本数据源,可能会沿途转换数据或提供其他功能。.

请注意FilterInputStream实现InputStream => Decorator实现Component,如UML图所示。.

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

BufferedInputStream将功能添加到另一个输入流 - 即缓冲输入并支持标记和重置方法的功能。.

所有ConcreteDecorators的示例:

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

工作示例代码:

我使用BufferedInputStream来读取Word的每个字符,该字符已存储在文本文件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. 应动态添加/删除对象职责和行为
  2. 具体实施应与责任和行为分离
  3. 当子类化成本太高而无法动态添加/删除职责时
13
Ravindra babu

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

8
Alex Aza

A - 装饰图案

A.1 - 装饰模式的用例

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

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

所以我们使用装饰器模式,将装饰器中的现有类包装起来。.

B - 基本GoF装饰器模式示例

这里我们有一个简单的接口和一个实现/具体类。接口有一个简单的方法,它是getMessageOfTheDay并返回一个String。假设有很多其他类使用此方法。因此,如果我们想要在实现/具体类中进行更改,它将影响旧的遗留代码。我们只想为新类更改它,因此我们使用装饰器模式。.

这是Gang Of Four装饰设计模式的一个简单例子;

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 - 演示代码:DecoratorDemo .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);
    }

}

看看这些例子。需要抽象装饰器类来包装原始合同和实现。使用抽象装饰器,你可以创建更新的多个装饰器,但是在这个例子中, BasicGreeter 被包装在抽象装饰器中,我们只在新的装饰器类上创建了 StrangeGreeter 。请通知装饰器类可以像火车一样使用,我们可以将装饰器包装在另一个装饰器内或相同。该功能是可扩展的,但原始类保留不做任何修改。.

C - OutputStream演示

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

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 Decorator输出:normal.txt

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

I love Commodore 64

D - JSON OutputStream装饰器演示

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

{
    data: <data here>
}

我想要的是将内容写在一个简单的字段 _ json _ format中。我们怎样才能实现这一目标?有许多琐碎的方式。但是,我将使用 GoF装饰模式 通过编写 JSONDecorator 扩展 OutputStream Java类;

D.1 - OutputStream的JSON Decorator: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装饰器演示: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 本身是一个Decorator模式,它是抽象装饰器,这里的具体装饰器是 JSONStream class。.

4
Levent Divilioglu

当您操作输入/输出流时,装饰器模式在Java.io类中使用(同样适用于读者和编写者)。.

inputstream,bytearrayinputstream,stringbuilderinputstreams等基于元素。 Filterinputstream是装饰器类的基类。过滤输入流(例如缓冲输入流)在读取流或写入流时可以执行其他操作。.

它们是通过封装流来构建的,并且是流本身。.

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

我想不出在Java.net中实现这种模式的任何类,但我认为你被告知这个包,因为它与Java.io(例如socket.getInputStream)紧密相连。.

实际上, 这是来自O'Relly的课程 解释了如何在Java.io中实现装饰器。.

此致,Stéphane

4
Snicolas

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

2
Will Johnson

好吧,我可能会迟到,但这个问题永远不会变老。理解 Decorator 的关键在于它使您能够将对象插入现有对象到另一个现有对象等等。在构造函数中实现此模式很受欢迎。例如,

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

如果你看一下维基百科中的图表,你会看到 ConcreteComponent Decorator 从同一个超类/接口继承, Component 。也就是说,这两个类具有相同的实现方法。.

但是,在 Decorator class中,你会看到一个箭头回到 Component ,这意味着你使用 Component somewhere in Decorator class。在这种情况下,您使用 Component 作为 Decorator 中构造函数的数据类型。这是一个重要的伎俩。如果没有这个技巧,您将无法将新对象插入现有对象。.

之后,您可以创建继承自 Decorator class的子类。因为所有类都具有相同的根,所以每个类都可以自由插入而无需任何顺序。.

2
kosalgeek

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

2
Chris Jester-Young