programing

IO 용 GoF 데코레이터 패턴의 사용 사례 및 예

sourcetip 2021. 1. 16. 11:17
반응형

IO 용 GoF 데코레이터 패턴의 사용 사례 및 예


wikipedia 에서 Decorator 패턴.NetJava IO 클래스에 사용 된다는 것을 읽었습니다 .

아무도 이것이 어떻게 사용되는지 설명 할 수 있습니까? 그리고 가능한 예를 통해 그것의 이점은 무엇입니까?

wikipedia Windows 양식 의 예가 있지만 Java IO 클래스에서 어떻게 발생하는지 알고 싶습니다 .


InputStream추상 클래스입니다. 같은 대부분의 콘크리트 구현 BufferedInputStream, GzipInputStream, ObjectInputStream, 등의 인스턴스를 사용하는 생성자가 같은 추상 클래스를. 이것이 데코레이터 패턴의 인식 키입니다 (이는 동일한 인터페이스의 인스턴스를 사용하는 생성자에도 적용됩니다).

이러한 생성자를 사용하면 모든 메서드가 래핑 된 인스턴스에 위임되며 메서드 동작 방식이 변경됩니다. 예를 들어 미리 메모리에 스트림을 버퍼링하거나 미리 스트림을 압축 해제하거나 스트림을 다르게 해석합니다. 일부는 마지막으로 래핑 된 인스턴스에 추가로 위임하는 추가 메서드도 있습니다. 이러한 메서드는 래핑 된 인스턴스를 추가 동작으로 장식합니다.

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();
// ...

장점은 필요에 따라 하나 이상의 다양한 데코레이터를 사용하여 스트림을 자유롭게 장식 할 수 있다는 것입니다. 그게 훨씬 더 같은 가능한 모든 조합에 대해 하나의 클래스를하는 것보다의 ObjectGzipBufferedFileInputStream, ObjectBufferedFileInputStream, GzipBufferedFileInputStream, ObjectGzipFileInputStream, ObjectFileInputStream, GzipFileInputStream, BufferedFileInputStream, 등

스트림을 닫으려고 할 때 가장 바깥 쪽 데코레이터를 닫는 것으로 충분합니다. 닫기 호출을 맨 아래까지 위임합니다.

ois.close();

또한보십시오:


Java IO 클래스를 살펴보기 전에 Decorator 패턴의 구성 요소를 이해해 보겠습니다 .

여기에 이미지 설명 입력

데코레이터 패턴에는 네 가지 구성 요소가 있습니다.

  1. 구성 요소 : 구성 요소 responsibilties가 동적으로 추가 할 수 있습니다 객체의 인터페이스를 정의
  2. ConcreteComponent : 단순히 컴포넌트 인터페이스 의 구현입니다.
  3. 장식 : 장식이 (A)에 기준 갖는 성분을 , 또한 따를 컴포넌트 인터페이스. 데코레이터는 본질적으로 컴포넌트를 래핑합니다.
  4. ConcreteDecorator는 : ConcreteDecorator은 단지 원래 책임을 추가 구성 요소 .

데코레이터 패턴은 특정 개체의 기능을 정적으로 확장 (장식)하는 데 사용할 수 있으며, 디자인 타임에 일부 기초 작업이 수행되는 경우 동일한 클래스의 다른 인스턴스와 독립적으로 런타임에 일부 경우에 확장 할 수 있습니다. 이것은 원래 클래스를 래핑 하는 새로운 Decorator 클래스를 디자인함으로써 달성됩니다 .

이제 이러한 개념을 java.io pacakge 클래스에 매핑 해 보겠습니다.

구성 요소:

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=> UML의 그림과 같이 데코레이터 구현하는 구성 요소 .

public class FilterInputStream
extends InputStream

ConcreteDecorator :

BufferedInputStream

BufferedInputStream은 다른 입력 스트림에 기능을 추가합니다. 즉, 입력을 버퍼링하고 표시 및 재설정 메서드를 지원하는 기능입니다.

모든 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. 개체 책임 및 동작은 동적으로 추가 / 제거되어야합니다.
  2. 구체적인 구현은 책임과 행동에서 분리되어야합니다.
  3. 하위 분류 비용이 너무 비싸서 책임을 동적으로 추가 / 제거 할 수없는 경우

.NET에는 BufferedStream, CryptoStream, GzipStream 등과 같은 스트림 데코레이터가 많이 있습니다 Stream. 이러한 데코레이터는 모두 클래스를 데코레이션 합니다.


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 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-데모 코드 : 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 데코레이터 출력 : normal.txt

There will be a new file with name "normal.txt" created under the project folder and the content will be;

I love Commodore 64

D - JSON OutputStream Decorator Demo

Now, I want to create a JSON wrapper format, which is as follows;

{
    data: <data here>
}

What I want is to write the content inside a simple one field JSON format. How can we achieve this goal? There are many trivial ways. However, I will use the GoF Decorator Pattern by writing a JSONDecorator which extends the OutputStream class of Java;

D.1 - JSON Decorator for OutputStream: 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 Demo: 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 Decorator Output: json.txt

{
    data:"I love Commodore 64"
}

Actually, OutputStream itself a Decorator Pattern, it is the abstract decorator and concrete decorator in here is the JSONStream class.


The decorator pattern is used in java.io classes when you manipulated input/output streams (and the same applies for readers and writers).

inputstream, bytearrayinputstream, stringbuilderinputstreams and so on are based elements. Filterinputstream is the base class for the decorator classes. Filter input streams (such as bufferedinput stream) can do additional things when they read streams or write to them.

They are built by encapsulating a stream, and are streams themselves.

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

I can't think of any class implementing this pattern in java.net, but I think your were told about this package as it is strongly tied to java.io (socket.getInputStream for instance).

Actually, here is a course from O'Relly that explains how decorator is implemented in java.io.

Regards, Stéphane


One way you can decorate an input/output stream is to apply compression/decompression to it. See the classes in java.util.zip, for example. Such a decorated stream can be used exactly the same way as a "regular" input/output stream, with compression/decompression performed totally transparently.


The decorator pattern is used to add functionality to existing objects such as a class defined in a library. You can then "decorate" it to fit your needs. If you are interested in learning more about patterns I recommend "Design Patterns" by the Gang of Four.


Well, I may be late to the party but this question never gets old. The key point to understand Decorator is that it gives you the ability to plug an object to an existing object to another existing object and so on. It is popular to implement this pattern in a constructor. For example,

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

If you look at the diagram in the wikipedia, you would see ConcreteComponent and Decorator inherit from the same superclass/interface, Component. That is, these two classes have the same implement method(s).

However, in the Decorator class, you'd see an arrow back to the Component, which means you use Component somewhere in the Decorator class. In this case, you use the Component as a datatype of a constructor in the Decorator. That is the big trick. Without this trick, you won't be able to plug a new object to an existing object.

After that, you can create subclasses inheriting from the Decorator class. Because all classes have the same root, every single classes can freely plugin without any order.

ReferenceURL : https://stackoverflow.com/questions/6366385/use-cases-and-examples-of-gof-decorator-pattern-for-io

반응형