web-dev-qa-db-ja.com

IOのGoFデコレータパターンの使用例と例

私は wikipedia を読みました装飾パターン。NetおよびJava IOクラス。

誰がこれがどのように使用されているか説明できますか?そして、可能な例でそれの利点は何ですか?

ウィキペディアにはWindowsフォームの例がありますが、Java IOでどのように起こるか知りたいですクラス。

52
DarthVader

InputStream は抽象クラスです。 BufferedInputStreamGzipInputStreamObjectInputStream などのような最も具体的な実装には、 same抽象クラスのインスタンスを取るコンストラクタ。これがデコレータパターンの認識キーです(これは、同じインターフェイスのインスタンスを取得するコンストラクタにも適用されます)。

このようなコンストラクターを使用すると、すべてのメソッドがラップされたインスタンスに委任され、メソッドの動作が変更されます。たとえば、ストリームを事前にメモリにバッファリングする、事前にストリームを圧縮解除する、ストリームを異なる方法で解釈するなどです。ラップされたインスタンスに最終的にさらに委任する追加のメソッドもあるものもあります。これらのメソッドは、ラップされたインスタンスを追加の動作で装飾します。

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

利点は、ニーズに合わせて1つ以上のさまざまなデコレータを使用してストリームを自由に装飾できることです。 ObjectGzipBufferedFileInputStreamObjectBufferedFileInputStreamGzipBufferedFileInputStreamObjectGzipFileInputStreamObjectFileInputStreamGzipFileInputStreamBufferedFileInputStreamなど.

ストリームを閉じようとしているときは、outermostデコレータを閉じるだけで十分です。 close呼び出しを最後まで委任します。

ois.close();

こちらもご覧ください:

133
BalusC

Decoratorパターンのコンポーネントを理解してから、Java IOクラス。

enter image description here

デコレーター パターンには4つのコンポーネントがあります

  1. Component:Componentは、動的に責任を追加できるオブジェクトのインターフェースを定義します
  2. ConcreteComponent:これはComponentインターフェースの単なる実装です
  3. Decorator:Decoratorには、Componentへの参照があり、準拠していますComponentインターフェースへ。デコレータは基本的にComponentをラップしています
  4. ConcreteDecorator:ConcreteDecoratorは、元のComponentに責任を追加するだけです。

デコレータパターンを使用して、特定のオブジェクトの機能を静的に拡張(装飾)することができます。または、場合によっては、実行時に、同じクラスの他のインスタンスとは独立して、設計時に何らかの基盤が提供されます。これは、元のクラスをラップする新しいDecoratorクラスを設計することで実現されます。

それでは、これらの概念をJava.ioパッケージクラスにマッピングしましょう。

コンポーネント:

InputStream

この抽象クラスは、バイトの入力ストリームを表すすべてのクラスのスーパークラスです。

InputStreamのサブクラスを定義する必要があるアプリケーションは、入力の次のバイトを返すメソッドを常に提供する必要があります。

public abstract int read()は抽象メソッドです。

ConcreteComponent:

FileInputStream

FileInputStreamは、ファイルシステム内のファイルから入力バイトを取得します。使用可能なファイルは、ホスト環境によって異なります。

FileInputStreamは、画像データなどの生バイトのストリームを読み取るためのものです。文字のストリームを読み取るには、FileReaderの使用を検討してください。

InputStreamのすべてのConcreteComponentsの例:

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

デコレーター:

FilterInputStream

FilterInputStreamには、基本的なデータソースとして使用する他の入力ストリームが含まれており、途中でデータを変換したり、追加機能を提供したりする場合があります。

FilterInputStreamInputStream =>を実装することに注意してください。デコレーターはUML図に示されているようにコンポーネントを実装します

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

BufferedInputStreamは、別の入力ストリームに機能を追加します。つまり、入力をバッファリングし、マークおよびリセットメソッドをサポートする機能です。

すべてのConcreteDecoratorsの例

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

動作サンプルコード:

BufferedInputStreamを使用して、テキストファイルa.txtに保存されているWordの各文字を読み取りました。

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. サブクラス化が費用がかかりすぎて動的に責任を追加/削除できない場合
14
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という1つの単純なメソッドがあり、Stringを返します。このメソッドを使用する他のクラスがたくさんあると仮定します。したがって、実装/コンクリートクラスを変更したい場合、古いレガシーコードに影響します。デコレータパターンを使用するために、新しいクラスのみに対して変更する必要があります。

ギャングオブフォーデコレーターデザインパターンの簡単な例を次に示します。

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

プロジェクトフォルダーの下に「normal.txt」という名前の新しいファイルが作成され、コンテンツが作成されます。

I love Commodore 64

D-JSON OutputStreamデコレーターデモ

次に、次のようなJSONラッパー形式を作成します。

{
    data: <data here>
}

私が欲しいのは、単純な1つのフィールド[〜#〜] json [〜#〜] format内にコンテンツを書き込むことです。どうすればこの目標を達成できますか?些細な方法がたくさんあります。ただし、OutputStream Javaクラスを拡張するJSONDecoratorを記述することにより、GoF Decorator Patternを使用します。

D.1-OutputStreamの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デコレーターデモ: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クラスです。

5

デコレータパターンは、入力/出力ストリームを操作するときにJava.ioクラスで使用されます(同じことがリーダーとライターにも当てはまります)。

inputstream、bytearrayinputstream、stringbuilderinputstreamsなどはベース要素です。 Filterinputstreamは、デコレータクラスの基本クラスです。フィルタ入力ストリーム(バッファリングされた入力ストリームなど)は、ストリームの読み取りまたは書き込み時に追加の処理を実行できます。

それらはストリームをカプセル化することで構築され、ストリームそのものです。

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

Java.netでこのパターンを実装するクラスは考えられませんが、Java.io(socket.getInputStreamなど)に強く結び付けられているため、このパッケージについて説明されたと思います。

実際、 ここはO'Rellyのコースです これはデコレータがJava.ioでどのように実装されているかを説明しています。

よろしく、ステファン

4
Snicolas

デコレータパターンは、ライブラリで定義されたクラスなどの既存のオブジェクトに機能を追加するために使用されます。その後、ニーズに合わせて「装飾」できます。パターンについて詳しく知りたい場合は、ギャングオブフォーの「デザインパターン」をお勧めします。

2
Will Johnson

まあ、私はパーティーに遅れることがありますが、この質問は決して古くなりません。 Decoratorを理解するための重要な点は、オブジェクトを既存のオブジェクトに、別の既存のオブジェクトにプラグインできるということです。このパターンをコンストラクターで実装するのが一般的です。例えば、

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

ウィキペディアの図を見ると、ConcreteComponentDecoratorが同じスーパークラス/インターフェースから継承されていることがわかりますComponent。つまり、これら2つのクラスには同じ実装メソッドがあります。

ただし、Decoratorクラスでは、Componentに戻る矢印が表示されます。つまり、ComponentDecoratorクラスのどこかに。この場合、ComponentDecoratorのコンストラクターのデータ型として使用します。それが大きなトリックです。このトリックがないと、新しいオブジェクトを既存のオブジェクトにプラグインできません。

その後、Decoratorクラスから継承するサブクラスを作成できます。すべてのクラスは同じルートを持っているため、すべてのクラスは順序なしで自由にプラグインできます。

2
kosalgeek

入出力ストリームを装飾する1つの方法は、圧縮/解凍を適用することです。たとえば、Java.util.Zipのクラスを参照してください。このような装飾されたストリームは、「通常の」入力/出力ストリームとまったく同じ方法で使用でき、圧縮/解凍は完全に透過的に実行されます。

2