web-dev-qa-db-ja.com

デコレータパターンとサブクラス化

サブクラス化を追加することで機能追加の問題を解決できますが、なぜデコレーターパターンを使用する必要があるのですか?デコレーターパターンの本当の利点は何ですか?

56
Gainster

から ウィキペディアのデコレータパターン

runtimeで特定のオブジェクトの機能を拡張(装飾)できるようにするために、デコレータパターンを使用できます。

デコレータパターンの要点は、動作/機能を動的に追加することです。これは、もちろん設計時には不可能です。

同じ記事から:

デコレータパターンは、サブクラス化の代替手段です。 サブクラス化はコンパイル時の動作を追加します。変更は元のクラスのすべてのインスタンスに影響します。 装飾は、個々のオブジェクトの実行時に新しい動作を提供できます。

41
sloth

GoFの例:

TextViewクラスがあるとします。次に、スクロールされたテキストビューが必要な場所で、TextViewをサブクラス化してScrolledTextViewクラスを作成します。また、他の場所では、テキストビューの周囲に境界線が必要です。したがって、もう一度サブクラス化してBorderedTextViewを作成します。さて、今どこかで境界線とスクロールの両方が必要です。前の2つのサブクラスには、両方の機能はありません。したがって、3つ目を作成する必要があります。 ScrolledBorderedTextViewを作成するとき、実際には作業を複製しています。前の2つの機能を構成する方法がある場合は、このクラスは必要ありません。まあ、物事は悪化する可能性があり、これらは不必要なクラスの爆発につながる可能性があります。

基本的に、デコレータパターンを使用することで、実行時にオブジェクトに任意の数の追加の責任を追加できますが、コード構造を損傷する可能性がない限り、サブクラス化では達成できません。

ただし、1つは、デザインパターンを使用する必要がないことです。パターンが必要かどうかは、特定の問題に依存します。コードを長期間維持するかどうか、拡張するかどうか、およびこれらのような他の多くの要因に依存します。そして、すべての場合に役立つパターンはありません。ある状況に適したパターン(デコレーターなど)は、別の状況には適さない場合があります。

45
taskinoor

GoFデザインパターンブック は、サブクラス化よりもデコレータを使用することの2つの主要な利点を示しています。

  1. 静的継承よりも柔軟性があります。デコレータパターンは、静的(複数)継承よりも柔軟にオブジェクトに責任を追加する方法を提供します。デコレーターを使用すると、実行時に責任を追加または削除するだけで、責任を追加および削除できます。対照的に、継承では、追加の責任ごとに新しいクラスを作成する必要があります(BorderedScrollableTextView、BorderedTextViewなど)。これにより多くのクラスが生じ、システムの複雑さが増します。さらに、特定のComponentクラスに異なるDecoratorクラスを提供することで、責任を混合して一致させることができます。

    また、デコレータを使用すると、プロパティを2回簡単に追加できます。たとえば、TextViewに二重の境界線を付けるには、2つのBorderDecoratorsをアタッチするだけです。 Borderクラスから2回継承すると、多くてもエラーが発生しやすくなります。

  2. 機能を含むクラスの上位階層を避けます。デコレータは、責任を追加するための従量課金制のアプローチを提供します。複雑でカスタマイズ可能なクラスで予測可能なすべての機能をサポートする代わりに、シンプルなクラスを定義して、Decoratorオブジェクトを使用して段階的に機能を追加できます。機能は単純な部分から構成できます。その結果、アプリケーションは、使用しない機能に料金を支払う必要がありません。また、予期しない拡張であっても、拡張するオブジェクトのクラスから独立して、新しい種類のデコレータを簡単に定義できます。複雑なクラスを拡張すると、追加する責任に関係のない詳細が公開される傾向があります。

私の見解では、サブクラスの爆発のみを防止することは非常に説得力があります。

水平スクロール、垂直スクロール、およびボーダーをすべて独立して追加したいTextWindowがある場合、オプションでサブクラスを使用して、HorizontalScrollingTextWindowVerticalScrollingTextWindowHorizontalAndVerticalScrollingTextWindowBorderedTextWindowHorizontalScrollingBorderedTextWindowVerticalScrollingBorderedTextWindowHorizontalAndVerticaScrollingBorderedTextWindowなど、スクロールの順序を気にする場合は、境界。

デコレータを使用すると、2つのスクロールデコレータと1つの境界デコレータを定義するだけで済みます。

19
Don Roby

サブクラス化は Liskov置換原理 の問題を引き起こす可能性があります。デコレータはこれを回避します。

デコレーターのもう1つの利点は、インターフェースに書き込む(強制的に書き込む)ことです。これにより、テストが容易になります。オブジェクト階層をインターフェイスに書き込むこともできるため、同じ利点がいくつかありますが、デコレータクラスの単一の実装を個別にテストできます。階層全体を常に基本クラスに戻すため、サブクラスでも同じことはできません。新しいコードを単独でテストできません。

デコレーターパターンを使用し、単一責任の原則に従って、いくつかのデコレーターを作成し、必要に応じてそれらをスタックすることができます。実行時に設定できます。継承では、可能なすべてのブランチを作成する必要があります(a-> b-> c、次にa-> c-> b、つまりコードを複製してテストの数を増やす)、または1つの階層を作成してから必要に応じて別の階層を追加しますが、これにより、新しいテスト/リリースサイクルがトリガーされます。

これが、サブクラス化の代わりにデコレータパターンを使用する理由です。

11
Aaron

実際の実装に基づく違いは次のとおりです。

装飾は、既存のクラスの機能を拡張するためのサブクラス化の代替方法です。これは、サブクラス化またはデコレーターを使用する必要があるいくつかのシナリオです。

1)サブクラス化は、古いクラスの機能だけでなくサブクラスの新機能も保持したいクラスの類似グループの機能を拡張する場合に主に使用され、サブクラスのすべてのインスタンスは同じ機能を共有します。子クラスを変更すると、子クラスのすべてのインスタンスが反映されます。階層関係シップなどのクラスの類似グループなどです。

親->子->孫.

車->マルチ800->マルチ100(新しいだけでなく、マルチ800の機能が追加されます)

2)デコレータパターンは、古い動作を変更せずに既存のクラスを装飾するために使用されます。クラスサークルにはプレーンボーダーがありますが、一部のユーザーが黄色でサークルを希望し、一部のユーザーがサークルを希望する後、赤いボーダーで装飾する必要があります。緑のボーダーで、一部のユーザーは赤と黄色のボーダーサークルを望んでおり、一部のユーザーは赤と緑のボーダーサークルなどを望んでいます。これは、組み合わせクラスを削減するのに最適なパターンです。以下に例を示します。

Icircle cir = new RedDecorator(new circle())は、サークルを赤い色で飾ります

Icircle cir = new YellowDecorator(new circle())は円を黄色で装飾します

Icircle cir = new RedDecorator(new YellowDecorator(new circle()))を飾る

ここでは、RedAndYellowクラスデコレータを作成する必要はありません。同じ方法で、新しい組み合わせクラスのセットを作成せずに、他の組み合わせのセットで円を装飾できます。

このようにして、組み合わせクラスの数を減らします。

ここにデコレータパターンの便利なリンクがあります

https://www.tutorialspoint.com/design_pattern/decorator_pattern.htm

2

柔軟性、それがIMOの理由です。

1
Jahan Zinedine