私は wikipedia を読みました装飾パターンは。NetおよびJava IOクラス。
誰がこれがどのように使用されているか説明できますか?そして、可能な例でそれの利点は何ですか?
ウィキペディアにはWindowsフォームの例がありますが、Java IOでどのように起こるか知りたいですクラス。
InputStream
は抽象クラスです。 BufferedInputStream
、 GzipInputStream
、 ObjectInputStream
などのような最も具体的な実装には、 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つ以上のさまざまなデコレータを使用してストリームを自由に装飾できることです。 ObjectGzipBufferedFileInputStream
、ObjectBufferedFileInputStream
、GzipBufferedFileInputStream
、ObjectGzipFileInputStream
、ObjectFileInputStream
、GzipFileInputStream
、BufferedFileInputStream
など.
ストリームを閉じようとしているときは、outermostデコレータを閉じるだけで十分です。 close呼び出しを最後まで委任します。
ois.close();
Decoratorパターンのコンポーネントを理解してから、Java IOクラス。
デコレーター パターンには4つのコンポーネントがあります
デコレータパターンを使用して、特定のオブジェクトの機能を静的に拡張(装飾)することができます。または、場合によっては、実行時に、同じクラスの他のインスタンスとは独立して、設計時に何らかの基盤が提供されます。これは、元のクラスをラップする新しいDecoratorクラスを設計することで実現されます。
それでは、これらの概念をJava.ioパッケージクラスにマッピングしましょう。
コンポーネント:
この抽象クラスは、バイトの入力ストリームを表すすべてのクラスのスーパークラスです。
InputStreamのサブクラスを定義する必要があるアプリケーションは、入力の次のバイトを返すメソッドを常に提供する必要があります。
public abstract int read()
は抽象メソッドです。
ConcreteComponent:
FileInputStreamは、ファイルシステム内のファイルから入力バイトを取得します。使用可能なファイルは、ホスト環境によって異なります。
FileInputStreamは、画像データなどの生バイトのストリームを読み取るためのものです。文字のストリームを読み取るには、FileReaderの使用を検討してください。
InputStreamのすべてのConcreteComponentsの例:
AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream,
InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream,
StringBufferInputStream
デコレーター:
FilterInputStreamには、基本的なデータソースとして使用する他の入力ストリームが含まれており、途中でデータを変換したり、追加機能を提供したりする場合があります。
FilterInputStream
はInputStream
=>を実装することに注意してください。デコレーターはUML図に示されているようにコンポーネントを実装します。
public class FilterInputStream
extends InputStream
ConcreteDecorator:
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);;
}
このパターンを使用する場合:
.NETには、BufferedStream、CryptoStream、GzipStreamなどのようなストリームデコレータがたくさんあります。これらはすべてStream
クラスをデコレートします。
デコレータパターンは、レガシークラスを変更せずにレガシー機能を拡張するために使用されます。インターフェースを実装する具体的なクラスがあるとします。ただし、既存のクラスとそのメソッドは既に他のクラスで使用されているため、既存のクラスに変更を加えたくないため、既存のメソッドの機能を拡張する必要があります。しかし、新しいクラスの拡張機能も必要ですが、この問題をどのように解決しますか?
1- We can't change the existing legacy code
2- We want to extend the functionality
したがって、デコレータパターンを使用して、デコレータ内に既存のクラスをラップします。
ここには、シンプルなインターフェイスと実装/コンクリートクラスがあります。インターフェイスには、getMessageOfTheDay
という1つの単純なメソッドがあり、String
を返します。このメソッドを使用する他のクラスがたくさんあると仮定します。したがって、実装/コンクリートクラスを変更したい場合、古いレガシーコードに影響します。デコレータパターンを使用するために、新しいクラスのみに対して変更する必要があります。
ギャングオブフォーデコレーターデザインパターンの簡単な例を次に示します。
public interface Greeter {
String getMessageOfTheDay();
}
public class BasicGreeter implements Greeter {
@Override
public String getMessageOfTheDay() {
return "Welcome to my server";
}
}
public abstract class GreeterDecorator implements Greeter {
protected Greeter greeter;
public GreeterDecorator(Greeter greeter) {
this.greeter = greeter;
}
public String getMessageOfTheDay() {
return greeter.getMessageOfTheDay();
}
}
public class StrangerDecorator extends GreeterDecorator {
public StrangerDecorator(Greeter greeter) {
super(greeter);
}
@Override
public String getMessageOfTheDay() {
return "Hello Stranger " + super.getMessageOfTheDay();
}
}
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という新しいデコレータクラスでのみ作成しました。デコレータクラスは電車のように使用できること、別のデコレータまたは同じデコレータ内にデコレータをラップできることを通知してください。機能は拡張可能ですが、元のクラスは変更なしで保持されます。
この例を見てみましょう。 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();
}
}
プロジェクトフォルダーの下に「normal.txt」という名前の新しいファイルが作成され、コンテンツが作成されます。
I love Commodore 64
次に、次のようなJSONラッパー形式を作成します。
{
data: <data here>
}
私が欲しいのは、単純な1つのフィールド[〜#〜] json [〜#〜] format内にコンテンツを書き込むことです。どうすればこの目標を達成できますか?些細な方法がたくさんあります。ただし、OutputStream Javaクラスを拡張するJSONDecoratorを記述することにより、GoF Decorator Patternを使用します。
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());
}
}
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();
}
}
{
data:"I love Commodore 64"
}
実際、OutputStreamそれ自体がデコレーターパターンです。抽象的なデコレーターであり、具体的なデコレーターはJSONStreamクラスです。
デコレータパターンは、入力/出力ストリームを操作するときにJava.ioクラスで使用されます(同じことがリーダーとライターにも当てはまります)。
inputstream、bytearrayinputstream、stringbuilderinputstreamsなどはベース要素です。 Filterinputstreamは、デコレータクラスの基本クラスです。フィルタ入力ストリーム(バッファリングされた入力ストリームなど)は、ストリームの読み取りまたは書き込み時に追加の処理を実行できます。
それらはストリームをカプセル化することで構築され、ストリームそのものです。
new BufferedReader( new FileInputStream() ).readLine();
Java.netでこのパターンを実装するクラスは考えられませんが、Java.io(socket.getInputStreamなど)に強く結び付けられているため、このパッケージについて説明されたと思います。
実際、 ここはO'Rellyのコースです これはデコレータがJava.ioでどのように実装されているかを説明しています。
よろしく、ステファン
デコレータパターンは、ライブラリで定義されたクラスなどの既存のオブジェクトに機能を追加するために使用されます。その後、ニーズに合わせて「装飾」できます。パターンについて詳しく知りたい場合は、ギャングオブフォーの「デザインパターン」をお勧めします。
まあ、私はパーティーに遅れることがありますが、この質問は決して古くなりません。 Decoratorを理解するための重要な点は、オブジェクトを既存のオブジェクトに、別の既存のオブジェクトにプラグインできるということです。このパターンをコンストラクターで実装するのが一般的です。例えば、
Icecream ic = new RainbowTopUp(new ChocoTopUp(new Vanilla()));
ウィキペディアの図を見ると、ConcreteComponentとDecoratorが同じスーパークラス/インターフェースから継承されていることがわかりますComponent。つまり、これら2つのクラスには同じ実装メソッドがあります。
ただし、Decoratorクラスでは、Componentに戻る矢印が表示されます。つまり、ComponentDecoratorクラスのどこかに。この場合、ComponentをDecoratorのコンストラクターのデータ型として使用します。それが大きなトリックです。このトリックがないと、新しいオブジェクトを既存のオブジェクトにプラグインできません。
その後、Decoratorクラスから継承するサブクラスを作成できます。すべてのクラスは同じルートを持っているため、すべてのクラスは順序なしで自由にプラグインできます。
入出力ストリームを装飾する1つの方法は、圧縮/解凍を適用することです。たとえば、Java.util.Zip
のクラスを参照してください。このような装飾されたストリームは、「通常の」入力/出力ストリームとまったく同じ方法で使用でき、圧縮/解凍は完全に透過的に実行されます。