通常、Decoratorパターンは、オブジェクトの現在のメソッドの1つを拡張することにより、オブジェクトの機能を拡張するために使用されます。
説明のために、オブジェクトobject
とデコレータdecorator
を検討してください。 object
には、methodA()
というメソッドが1つあります。 decorator
は、Decoratorパターンであるため、継承によるこのメソッドも持っています(object
とdecorator
の両方は、最終的にmethodA()
。AbstractObject
と呼びましょう。
object
のmethodA()
は次のようになります。
_public void methodA(){
// do stuff
}
_
decorator
のmethodA()
は次のようになります。
_public void methodA(){
// do some extra stuff.
wrappedObject.methodA(); // and then delegate to the wrapped object.
}
_
これは次のように使用されます。
_AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object.methodA(); // does stuff and some more stuff. the extended methodA().
_
decorator
のポイントは、object
の機能を拡張することですこれは、object
の現在のメソッドの1つを拡張することによってこれを行います。これまでのところ、クラシックなデコレータ。
今、私の質問:
前述したように、Decoratorは通常、オブジェクトの現在のメソッドの1つにコンテンツを追加することにより、オブジェクトの機能を拡張します。
しかし、Decoratorを使用してオブジェクトにパブリックメソッドを追加したい場合はどうなりますか?
decorator
に別のメソッドmethodB()
がある次のシナリオを検討してください:
_AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
((ConcreteDecorator)object).methodB();
// methodB() belongs to ConcreteDecorator() but not to AbstractObject().
_
これを実行したいかどうかを確認するには、object
をConcreteDecorator
にキャストする必要があります。
これはobject
の具象型とそれが装飾されているという事実を公開しているため、Decoratorパターンの意図をいくぶん打ち負かしています。また、methodB()
を呼び出すコードは、object
が装飾されていることを知る必要があります。繰り返しになりますが、Decoratorの精神はあまりありません。
これに照らして2つの質問があります:
この方法でデコレータパターンを使用したことはありますか?現在のメソッドの「拡張」だけではなく、オブジェクトにパブリックメソッドを追加するために使用されたことはありますか?
これがDecoratorの良いアイデアや賢い使い方になる状況はありますか?
デコレータパターンは通常、サブクラスの急増を回避するために使用されます。一般的な例には、n個の属性(スクロールバー、タイトルバー、サイズ変更可能、移動可能など)の任意の組み合わせが必要になる可能性があるUIウィンドウが含まれます。これらのn個の属性のすべての組み合わせをサポートするには、2 ^ n個のサブクラスを作成する必要があります。デコレータパターンは、ベースウィンドウクラスのexistingパブリックメソッドをラップすることで爆発を防ぎます。この場合、デコレータパターンはn個のクラスのみを必要とするのが理想的です。
説明したケースでは、newパブリックメソッドを追加しています。したがって、デコレータパターンはおそらく理想的ではありません。デコレーターを介して新しいメソッドを追加するのが理想的な状況は考えられません。
考慮すべきいくつかの選択肢:
SOLIDの原則を念頭に置いている限り、サブタイピングを使用してメソッドを追加することは有効です。そのため、methodBをサポートするために具象オブジェクトを拡張し、その周囲のデコレータを設計することを検討してください。
AbstractObject object = new ExtendedConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
(ConcreteDecorator)object.methodB();
// methodB() belongs to ExtendedConcreteObject() but not to AbstractObject()
問題を表示する別の方法:パブリックメソッドを追加することにより、オブジェクトのインターフェイスを変更します。これは、 adapter パターンを使用して実行できます。具象デコレータをシンプルに保つために、 decorator を作成してから、そのデコレータ用のアダプタを構築することを検討できます。
AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object = new ConcreteAdapter(object);
(ConcreteAdapter)object.methodB();
// methodB() belongs to ConcreteAdapter() but not to AbstractObject() or ConcreteDecorator().
装飾とは、基礎となるクラスに微妙な動作の変更を追加することです。
あなたの意図は実際にクラスを拡張することだと思います。 「懸念の分離」を考えてください。また、「オープン/クローズド」の原則が思い浮かびます(私は個人的には完全に同意しません)
装飾のポイントは、特定のインターフェイスをサポートするオブジェクトを渡すことです。追加のメソッドで追加の処理を行う場合、アプリケーションはそのオブジェクトを「ダウンキャスト」して、追加の動作を取得する必要があります。
一部のエンジニアへのダウンキャストは問題ないようですが、他のエンジニアは、コードがinstanceofキーワードで散らかされているため、これはクリーンなソリューションではないと感じています。これにより、リファクタリングの際にさらに注意を要する拡張実装にクラスが関連付けられます。
個人的には、オブジェクトのダウンキャストは控えめに使用する必要があり、インターフェースを破壊するため、消費クラスからできるだけ隠す必要があります。
私が言っているのは、それは本当に主観的な質問だということです。注意してください。