デコレータパターン

オブジェクト指向プログラミングにおいてデコレータパターンは、同じクラスの他のインスタンスの動作に影響を与えずに、個々のオブジェクトに動的に動作を追加できる設計パターンです[1]デコレータパターンは、異なる関心を持つクラス間に機能を分散できるため、単一責任の原則に準拠するのに役立ちます。[2]また、クラスの機能をソースコードを変更せずに拡張できるため、オープンクローズ原則もサポートしています。 [3 ]デコレータを使用すると、まったく新しいクラス階層を作成しなくても、オブジェクトの動作を実行時に拡張または組み合わせることができるため、サブクラス化よりも柔軟で効率的です。

概要

デコレータ[4]デザインパターンは、23のGang-of-Fourデザインパターンの1つです。これらは、繰り返し発生する設計上の問題を解決し、柔軟で再利用可能なオブジェクト指向ソフトウェア(つまり、実装、変更、テスト、再利用が容易なオブジェクト)を設計する方法を説明しています。

デコレータパターンは、サブクラス化に代わる柔軟な機能拡張方法を提供します。サブクラス化では、異なるサブクラスがそれぞれ異なる方法でクラスを拡張します。ただし、拡張はコンパイル時にクラスにバインドされ、実行時には変更できません。デコレータパターンを使用すると、実行時にオブジェクトに動的に責任を追加(または削除)できます。これは、Decorator以下のオブジェクト を定義することで実現されます。

  • すべてのリクエストを転送することで、拡張(装飾)オブジェクト( )のインターフェースをComponent透過的に実装します。
  • リクエストを転送する前または転送後に追加機能を実行します。

これにより、さまざまなDecoratorオブジェクトを操作して、実行時にオブジェクトの機能を動的に拡張することができます。[5]

意図

デコレータUMLクラス図

デコレータパターンは、設計時に何らかの準備作業が行われていれば、特定のオブジェクトの機能を静的に拡張(デコレート)したり、場合によっては実行時に、同じクラスの他のインスタンスとは独立して拡張(デコレート)したりするために使用できます。これは、元のクラスをラップする新しいデコレータクラスを設計することで実現されます。このラップは、以下の一連の手順で実現できます。

  1. 元のComponentクラスをDecoratorクラスにサブクラス化します (UML 図を参照)。
  2. Decoratorクラスでコンポーネントポインターをフィールドとして追加します。
  3. DecoratorクラスではComponent をDecoratorコンストラクターに渡して、 Componentポインターを初期化します
  4. DecoratorクラスではすべてのComponentメソッドをComponentポインタに転送します
  5. ConcreteDecorator クラスで、動作を変更する必要があるすべてのComponentメソッドをオーバーライドします。

このパターンは、複数のデコレータを積み重ねて、そのたびにオーバーライドされたメソッドに新しい機能を追加できるように設計されています。

デコレータと元のクラスオブジェクトは共通の機能セットを共有していることに注意してください。前の図では、operation() メソッドはデコレータ付きバージョンとデコレータなしバージョンの両方で使用できました。

デコレーション機能(メソッド、プロパティ、その他のメンバーなど)は通常、インターフェース、ミックスイン(別名トレイト)、またはクラス継承によって定義され、デコレータとデコレーション対象オブジェクトで共有されます。前の例では、 Componentクラスは ConcreteComponent とDecoratorのサブクラスの両方に継承されています。

デコレータパターンはサブクラス化の代替手段です。サブクラス化はコンパイル時に動作を追加し、その変更は元のクラスのすべてのインスタンスに影響します。一方、デコレータパターンは、選択したオブジェクトに対して実行時に新しい動作を提供できます[5]

この違いは、機能拡張に複数の独立した方法がある場合に最も重要になります。一部のオブジェクト指向プログラミング言語では、実行時にクラスを作成できず、設計時にどのような拡張の組み合わせが必要になるかを予測することが通常できません。つまり、あらゆる組み合わせに対して新しいクラスを作成する必要があるということです。一方、デコレータは実行時に作成されるオブジェクトであり、使用ごとに組み合わせることができます。Java.NET Frameworkの両方のI/Oストリーム実装にはデコレータパターンが組み込まれています。[5]

モチベーション

ウィンドウの例の UML 図

例として、ウィンドウシステム内のウィンドウを考えてみましょう。ウィンドウの内容をスクロールできるようにするには、必要に応じて水平または垂直のスクロールバーを追加したい場合があります。ウィンドウはWindowインターフェースのインスタンスで表され、このクラスにはスクロールバーを追加する機能がないものとします。スクロールバーを追加するScrollingWindowのサブクラスを作成するか、既存のWindowオブジェクトにこの機能を追加するScrollingWindowDecoratorを作成することができます。この時点では、どちらの解決策でも問題ありません。

ここで、ウィンドウに境界線を追加する機能も必要だと仮定します。この場合も、元のWindowクラスではサポートされていません。ScrollingWindowサブクラスは、事実上新しい種類のウィンドウを作成しているため、問題を引き起こします。すべてのウィンドウではなく多数のウィンドウに境界線のサポートを追加したい場合はサブクラスWindowWithBorderScrollingWindowWithBorderなどを作成しなければなりません。この問題は、新しい機能やウィンドウのサブタイプが追加されるたびに悪化します。デコレータ ソリューションの場合は、新しいBorderedWindowDecoratorが作成されます。ScrollingWindowDecoratorまたはBorderedWindowDecoratorの任意の組み合わせで、既存のウィンドウを装飾できます。機能をすべてのウィンドウに追加する必要がある場合は、基本クラス変更できます。一方、場合によっては (外部フレームワークを使用する場合など)、基本クラスを変更することが不可能、合法、または不便な場合があります。

前の例では、SimpleWindowクラスとWindowDecoratorクラスはWindowインターフェイスを実装し、このシナリオでウィンドウ コントロールを装飾するために必要なdraw()メソッドとgetDescription()メソッドを定義しています。

一般的なユースケース

デコレータの適用

コマンド(ボタンの押下など)によるデコレータの追加または削除は一般的なUIパターンであり、多くの場合、コマンド設計パターンと共に実装されます。例えば、テキスト編集アプリケーションにはテキストをハイライト表示するボタンがあります。ボタンが押されると、現在選択されている個々のテキストグリフはすべて、draw()関数を変更するデコレータでラップされ、ハイライト表示された状態で描画されます(実際の実装では、効率を最大化するために境界設定システムも使用される可能性があります)。

状態の変化に基づいてデコレータを適用または削除することも、一般的なユースケースの一つです。状態のスコープに応じて、デコレータを一括で適用または削除できます。同様に、Stateデザインパターンは、変化する機能をカプセル化するサブクラス化されたオブジェクトの代わりに、デコレータを使用して実装できます。このようにデコレータを使用することで、Stateオブジェクトの内部状態と機能はより構成的になり、任意の複雑さに対応できるようになります。

Flyweightオブジェクトでの使用

デコレーションは、 Flyweight デザイン パターンでもよく使用されます。Flyweight オブジェクトは、2 つのコンポーネントに分かれています。1 つはすべての Flyweight オブジェクト間で共有される不変コンポーネント、もう 1 つは部分的に共有されることも完全に共有されないこともある、装飾されたバリアント コンポーネントです。Flyweight オブジェクトのこの分割は、メモリ消費量を削減するためのものです。デコレータも通常キャッシュされ、再利用されます。デコレータにはすべて、共有された不変オブジェクトへの共通参照が含まれます。装飾された状態が部分的にのみ可変である場合、デコレータもある程度共有できますが、使用中に状態を変更しないように注意する必要があります。iOS の UITableView は、この方法で Flyweight パターンを実装しています。つまり、テーブルビューの再利用可能なセルは、共通のテーブルビュー行オブジェクトへの参照を含むデコレータであり、セルはキャッシュ/再利用されます。

デコレータとのインターフェースの障害

オブジェクトのコレクションに様々な方法でデコレータを組み合わせて適用すると、デコレータによって追加された機能を最大限に活用しながらコレクションとインターフェースする際に問題が生じます。このような場合には、アダプタパターンビジターパターンの使用が効果的です。複数の層のデコレータとのインターフェースは新たな課題をもたらすため、アダプタとビジターのロジックはそれを考慮して設計する必要があります。

建築の関連性

デコレータは、機能拡張においてトップダウン型ではなく階層型のアプローチをサポートします。デコレータを使用すると、実行時にインターフェースの動作を追加または変更することができます。デコレータは、オブジェクトを多層的かつ任意の組み合わせでラップするために使用できます。サブクラスで同じことを行うには、複雑な多重継承ネットワークを実装する必要があり、メモリ効率が悪く、ある時点ではスケーリングが不可能になります。同様に、同じ機能をプロパティで実装しようとすると、オブジェクトの各インスタンスが不要なプロパティで肥大化します。上記の理由から、デコレータはサブクラス化よりもメモリ効率の高い代替手段と見なされることがよくあります。

デコレータは、サブクラス化できないオブジェクト、実行時に特性を変更する必要があるオブジェクト (他の箇所で説明されているように)、または一般的に必要な機能が不足しているオブジェクトを特殊化するためにも使用できます。

APIの強化における使用法

デコレータ パターンは、ファサード パターンを拡張することもできます。ファサードは、カプセル化する複雑なシステムと単純にインターフェースするように設計されていますが、システムに機能を追加するわけではありません。ただし、複雑なシステムをラップすることで、システム内のサブコンポーネントの調整に基づいて新しい機能を導入するためのスペースが提供されます。たとえば、ファサード パターンは、1 つの多言語辞書インターフェースの下に、さまざまな言語の辞書を統合できます。新しいインターフェースは、言語間で単語を翻訳するための新しい機能も提供できます。これはハイブリッド パターンです。統合されたインターフェースが拡張のためのスペースを提供します。デコレータは、個々のオブジェクトのラップに限定されず、このハイブリッド アプローチでオブジェクトのクラスターをラップすることもできると考えてください。

デコレータの代替

デコレータパターンの代替として、ラッパーが特定のインターフェースを尊重し、ポリモーフィックな動作をサポートする必要がある場合にはアダプタを使用できます。また、基になるオブジェクトへのより簡単でシンプルなインターフェースが必要な場合にはファサードを使用できます。 [6]

パターン意図
アダプタクライアントが期待するものと一致するように、あるインターフェースを別のインターフェースに変換します
デコレーター元のコードをラップすることでインターフェースに動的に責任を追加します
ファサード簡素化されたインターフェースを提供する

構造

UMLクラス図とシーケンス図

DecoratorデザインパターンのUMLクラス図とシーケンス図のサンプル。 [7]

上記のUML クラス図では、抽象クラスは装飾されたオブジェクト()へのDecorator参照()を保持し、すべてのリクエストをそのオブジェクト()に転送します。これにより、 はクライアントに対して透過的(不可視)になりますcomponentComponentcomponent.operation()DecoratorComponent

サブクラス ( Decorator1、 )は、 に追加されるDecorator2動作 ( ) を実装します(リクエストを に転送する前/後)。シーケンス図は実行時の相互作用を示しています。オブジェクトはおよびオブジェクトを介してオブジェクトの機能を拡張します は を呼び出しはリクエストを に転送します。 はリクエストを に転送した後を実行し、 に戻ります を実行して に戻ります。addBehavior()Component
ClientDecorator1Decorator2Component1
Clientoperation()Decorator1Decorator2Decorator2addBehavior()Component1Decorator1addBehavior()Client

C++

この実装 (C++23 の機能を使用) は、本書の C++98 以前の実装に基づいています。

stdをインポートします std :: unique_ptrを使用します //飲料インターフェース。class Beverage { public : virtual void drink () = 0 ; virtual ~ Beverage () = default ; };           //デコレーションできるドリンク。class Coffee : public Beverage { public : virtual void drink () override { std :: print ( "Drinking Coffee" ); } };           クラスSoda : public Beverage { public : virtual void drink () override { std :: print ( "Drinking Soda" ); } };           クラスBeverageDecorator : public Beverage { private : unique_ptr < Beverage > component ; protected : void callComponentDrink () { if ( component ) { component -> drink (); } } public : BeverageDecorator () = delete ;                   明示的なBeverageDecorator ( unique_ptr < Beverage > component ) : component { std :: move ( component )} {} virtual void drink () = 0 ; };           クラスMilk : public BeverageDecorator { private : float percentage ; public : Milk ( unique_ptr < Beverage > component , float percentage ) : BeverageDecorator ( std :: move ( component )), percentage { percentage } {}              virtual void drink () override { callComponentDrink (); std :: print ( ", with milk of richness {}%" , percentage ); } };        クラスIceCubes : public BeverageDecorator { private : int count ; public : IceCubes ( unique_ptr < Beverage > component , int count ) : BeverageDecorator ( std :: move ( component )), count { count } {}              virtual void drink () override { callComponentDrink (); std :: print ( ", with {} ice cubes" , count ); } };        クラスSugar : public BeverageDecorator { private : int spoons = 1 ; public : Sugar ( unique_ptr < Beverage > component , int spoons ) : BeverageDecorator ( std :: move ( component )), spoons { spoons } {} virtual void drink () override { callComponentDrink (); std :: print ( ", with {} spoons of sugar" , spoons ); } };                         int main ( int argc , char * argv []) { unique_ptr < Beverage > soda = std :: make_unique < Soda > (); soda = std :: make_unique < IceCubes > ( std :: move ( soda ), 3 ); soda = std :: make_unique < Sugar > ( std :: move ( soda ), 1 );                   soda -> drink (); std :: println (); unique_ptr < Beverage > coffee = std :: make_unique < Coffee > (); coffee = std :: make_unique < IceCubes > ( std :: move ( coffee ), 16 ); coffee = std :: make_unique < Milk > ( std :: move ( coffee ), 3. ); coffee = std :: make_unique < Sugar > ( std :: move ( coffee ), 2 );                   コーヒー->飲み物(); 0を返す; } 

プログラムの出力は次のようになります

ソーダ飲む3砂糖1コーヒー飲む16濃度3 ミルク砂糖2                         

完全な例は godbolt ページでテストできます。

C++

ここでは 2 つのオプションが提示されています。1 つ目は、動的で実行時に構成可能なデコレータ (明示的にプロキシされない限り、デコレータ関数の呼び出しに問題があります)、もう 1 つはミックスイン継承を使用するデコレータです。

ダイナミックデコレータ

stdをインポートします std :: stringを使用します クラスShape { public : virtual ~ Shape () = default ; virtual string getName () const = 0 ; };            クラスCircle : public Shape { private : float radius = 10.0f ; public : void resize ( float factor ) noexcept { radius *= factor ; }                    [[ nodiscard ]] string getName () const override { return std :: format ( "半径 {} の円" , radius ); } };         クラスColoredShape : public Shape { private : string color ; Shape & shape ; public : ColoredShape ( const string & color , Shape & shape ) : color { color }, shape { shape } {}                  [[ nodiscard ]] string getName () const override { return std :: format ( "{} は色付き {}" shape . getName ()、color ); } };          int main () { Circle circle ; ColoredShape colouredShape { "red" , circle }; std :: println ( "{}" , colouredShape . getName ()); }         
stdをインポートします std :: stringを使用します。std :: unique_ptr使用します  クラスWebPage { public : virtual void display () = 0 ; virtual ~ WebPage () = default ; };           クラスBasicWebPage : public WebPage { private : string html ; public : void display () override { std :: println ( "基本的な WEB ページ" ); } };            クラスWebPageDecorator : public WebPage { private : unique_ptr < WebPage > webPage ; public : explicit WebPageDecorator ( unique_ptr < WebPage > webPage ) : webPage { std :: move ( webPage )} {}             void display ()オーバーライド{ webPage -> display (); } };     クラスAuthenticatedWebPage : public WebPageDecorator { public : explicit AuthenticatedWebPage ( unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {}          void authenticateUser () { std :: println ( "認証が完了しました" ); }     void display ()オーバーライド{ authenticateUser (); WebPageDecorator :: display (); } };      クラスAuthorizedWebPage : public WebPageDecorator { public : explicit AuthorizedWebPage ( unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {}          void authorizedUser () { std :: println ( "承認が完了しました" ); }     void display ()オーバーライド{ authorizedUser (); WebPageDecorator :: display (); } };      int main ( int argc , char * argv []) { unique_ptr < WebPage > myPage = std :: make_unique < BasicWebPage > ();          myPage = std :: make_unique < AuthorizedWebPage > ( std :: move ( myPage )); myPage = std :: make_unique < AuthenticatedWebPage > ( std :: move ( myPage )); myPage -> display (); std :: println (); return 0 ; }         

静的デコレータ(ミックスイン継承)

この例では、テンプレート引数から継承できる C++ の機能により可能になる静的 Decorator 実装を示します。

stdをインポートします std :: stringを使用します クラスCircle { private : float radius = 10.0f ; public : void resize ( float factor ) noexcept { radius *= factor ; }                  [[ nodiscard ]] string getName () const { return std :: format ( "半径 {} の円" , radius ); } };        テンプレート< typename T >クラスColoredShape : public T { private : string color ; public : explicit ColoredShape ( const string & color ) : color { color } {} [[ nodiscard ]] string getName () const { return std :: format ( "{} は色付き {}" , T :: getName (), color ); } };                          int main () { ColoredShape < Circle > redCircle { "red" }; std :: println ( "{}" , redCircle . getName ()); redCircle . resize ( 1.5f ); std :: println ( "{}" , redCircle . getName ()); }         

ジャワ

最初の例(ウィンドウ/スクロールシナリオ)

次の Java の例は、ウィンドウ/スクロール シナリオを使用したデコレータの使用を示しています。

// ウィンドウ インターフェース クラスpublic interface Window { void draw (); // ウィンドウを描画しますString getDescription (); // ウィンドウの説明を返します}         // スクロールバーのないシンプルなウィンドウの実装class  SimpleWindow implements Window { @Override public void draw () { // ウィンドウを描画する}           @Override public String getDescription () { return "simple window" ; } }       

Window次のクラスには、デコレータ クラス自体を含むすべてのクラスのデコレータが含まれています。

// 抽象デコレータクラス - Window を実装していることに注意してください。抽象クラスWindowDecorator はWindowを実装します{ private final Window windowToBeDecorated ; // 装飾されるウィンドウ           パブリックWindowDecorator (ウィンドウwindowToBeDecorated ) { this . windowToBeDecorated = windowToBeDecorated ; }        @Override public void draw () { windowToBeDecorated . draw (); // 委任}        @Override public String getDescription () { return windowToBeDecorated . getDescription (); // 委任} }        // 垂直スクロールバー機能を追加する最初の具体的なデコレータclass  VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); }          @Override public void draw () { super . draw (); drawVerticalScrollBar (); }        private void drawVerticalScrollBar () { // 垂直スクロールバーを描画する}      @Override public String getDescription () { return String . format ( "%s、垂直スクロールバーを含む" , super . getDescription ()); } }        // 水平スクロールバー機能を追加する 2 番目の具体的なデコレータclass  Horizo​​ntalScrollBarDecorator extends WindowDecorator { public Horizo ​​ntalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); }          @Override public void draw () { super . draw (); drawHorizo​​ntalScrollBar (); }        private void drawHorizo​​ntalScrollBar () { // 水平スクロールバーを描画する}      @Override public String getDescription () { return String . format ( "%s、水平スクロールバーを含む" super . getDescription ()); } }        

Window以下は、完全に装飾されたインスタンス (つまり、垂直および水平スクロールバー付き) を作成し、その説明を出力するテスト プログラムです。

public class DecoratedWindowTest { public static void main ( String [] args ) { // 水平および垂直スクロールバーを備えた装飾されたウィンドウを作成します。Window decoratedWindow = new Horizo ​​ntalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ()) );                     //ウィンドウの説明を出力ます。System.out.println ( decoratedWindow.getDescription ( ) ) ; } }  

このプログラムの出力は「垂直スクロールバーと水平スクロールバーを含むシンプルなウィンドウ」です。2getDescriptionつのデコレータのメソッドが、まずデコレートされたWindowの説明を取得し、それを接尾辞で装飾している点に注目してください。

以下はテスト駆動開発のためのJUnitテストクラスです。

org.junit.Assertをインポートします。org.junit.Testインポートします  public class WindowDecoratorTest { @Test public void testWindowDecoratorTest () { Window decoratedWindow = new Horizo ​​ntalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ()) ); // 説明に水平 + 垂直スクロールバーが実際に含まれていることを確認しますAssert . assertEquals ( "simple window, including vertical scrollbars, including horizo​​ntal scrollbars" , decoratedWindow . getDescription ()); } }                  

2番目の例(コーヒーを作るシナリオ)

次のJavaの例では、コーヒーを作るシナリオでデコレータを使用する方法を示します。この例では、シナリオにはコストと材料のみが含まれています。

// インターフェース Coffee は、デコレータによって実装された Coffee の機能を定義しますpublic interface Coffee { public double getCost (); // コーヒーのコストを返しますpublic String getIngredients (); // コーヒーの材料を返します}           // 追加の材料なしのシンプルなコーヒーの拡張public class SimpleCoffee implements Coffee { @Override public double getCost () { return 1 ; }              @Override public String getIngredients () { return "Coffee" ; } }       

次のクラスには、デコレータ クラス自体を含む、 すべてのCoffeeクラスのデコレータが含まれています。

// 抽象デコレータ クラス - Coffee インターフェイスを実装していることに注意してくださいpublic abstract class CoffeeDecorator implements Coffee { private final Coffee decoratedCoffee ;           パブリックCoffeeDecorator ( Coffee c ) { this .decoratedCoffee = c ; }        @Override public double getCost () { // インターフェースの実装メソッドreturn decoratedCoffee . getCost (); }         @Override public String getIngredients () { return decoratedCoffee . getIngredients (); } }       // デコレータ WithMilk はコーヒーにミルクを混ぜます。// CoffeeDecoratorを拡張していることに注意してください。class WithMilk extends CoffeeDecorator { public WithMilk ( Coffee c ) { super ( c ); }           @Override public double getCost () { // 抽象スーパークラスで定義されたメソッドをオーバーライドします。 return super . getCost () + 0.5 ; }           @Override public String getIngredients () { return String . format ( "%s, Milk" , super . getIngredients ()); } }        // デコレータ WithSprinkles はコーヒーにスプリンクルを混ぜます。// CoffeeDecorator を拡張していることに注意してください。class WithSprinkles extends CoffeeDecorator { public WithSprinkles ( Coffee c ) { super ( c ); }           @Override public double getCost () { return super . getCost () + 0.2 ; }          @Override public String getIngredients () { return String . format ( "%s, Sprinkles" , super . getIngredients ()); } }        

以下は、完全にデコレーションされた (ミルクとスプリンクルで) Coffeeインスタンスを作成し、コーヒーのコストを計算してその材料を出力するテスト プログラムです。

public class Main { public static void printInfo ( Coffee c ) { System . out . printf ( "コスト: %s; 材料: %s%n" , c . getCost (), c . getIngredients ()); }              パブリック静的void main String [] args { Coffee c = new SimpleCoffee (); printInfo c );            c = new WithMilk ( c ); printInfo ( c );     c = new WithSprinkles ( c ); printInfo ( c ); } }     

このプログラムの出力は次のとおりです。

コスト: 1.0; 材料: コーヒーコスト: 1.5; 材料: コーヒー、ミルク費用: 1.7; 材料: コーヒー、牛乳、スプリンクル

PHP

抽象 クラス Component {  protected  $data ;  protected  $value ; 抽象 パブリック 関数 getData (); 抽象 パブリック 関数 getValue (); }クラス ConcreteComponent は Componentを拡張します { public function __construct () { $this -> value = 1000 ; $this -> data = "具体的なコンポーネント: \t { $this -> value } \n " ; }            パブリック 関数 getData ()  {  $this -> dataを返します ; }  パブリック 関数 getValue ()  {  $this -> valueを返します ; } } 抽象 クラス Decorator は Componentを拡張します { }クラス ConcreteDecorator1 は Decorator を拡張します { public function __construct ( Component $data ) { $this -> value = 500 ; $this -> data = $data ; }             public  function  getData ()  {  return  $this -> data -> getData ()  .  "具体的なデコレータ1: \t { $this -> value } \n " ;  } パブリック 関数 getValue ()  { 戻り値 $this -> value  +  $this -> data -> getValue ();  } }クラス ConcreteDecorator2 は Decoratorを拡張します { public function __construct ( Component $data ) { $this -> value = 500 ; $this -> data = $data ; }             public  function  getData ()  {  return  $this -> data -> getData ()  .  "具体的なデコレータ2: \t { $this -> value } \n " ;  } パブリック 関数 getValue ()  { 戻り値 $this -> value  +  $this -> data -> getValue ();  } }クラス Client { プライベート $component ; パブリック 関数 __construct ()  {  $this -> component  =  new  ConcreteComponent ();  $this -> component  =  $this -> wrapComponent ( $this -> component ); echo  $this -> component -> getData ();  echo  "Client: \t\t\t " ;  echo  $this -> component -> getValue ();  } プライベート 関数 wrapComponent ( Component  $component )  {  $component1  =  new  ConcreteDecorator1 ( $component );  $component2  =  new  ConcreteDecorator2 ( $component1 );  return  $component2 ;  } }$client  = 新しい クライアント();// 結果: #quanton81//コンクリートコンポーネント: 1000 //コンクリートデコレータ1: 500 //コンクリートデコレータ2: 500 //クライアント: 2000

パイソン

Python Wiki - DecoratorPattern から引用した次の Python の例は、デコレータをパイプライン化してオブジェクトに多くの動作を動的に追加する方法を示しています。

""" 0~255 の値を持つ 10x10 グリッドの世界におけるデコレータのデモを示します。"""入力からランダムにインポートし、任意のインポート    def s32_to_u16 ( x : int ) -> int : sign : int x < 0の場合: sign = 0xF000それ以外の場合: sign = 0 bottom : int = x & 0x00007FFF bottom | sign を返します                           seed_from_xy ( x : int , y : int ) -> int :戻りs32_to_u16 ( x ) | ( s32_to_u16 ( y ) << 16 )            クラスRandomSquare : def __init__ ( self , seed_modifier : int ) - > None : self.seed_modifier : int = seed_modifier            def get ( self , x : int , y : int ) -> int : seed : int = seed_from_xy ( x , y ) ^ self . seed_modifier random . seed ( seed ) return random . randint ( 0 , 255 )                  クラスDataSquare : def __init__ ( self , initial_value : int = None ) - > None : self.data : list [ int ] = [ initial_value ] * 10 * 10                  def get ( self , x : int , y : int ) -> int : return self . data [( y * 10 ) + x ] # はい、これらはすべて 10x10 です               def set ( self , x : int , y : int , u : int ) -> None : self . data [( y * 10 ) + x ] = u                クラスCacheDecorator : def __init__ ( self , decorated : Any ) - > None : self.decorated : Any = decorated self.cache : DataSquare = DataSquare ( )                def get ( self , x : int , y : int ) - > int : if self.cache.get ( x , y ) == None : self.cache.set ( x , y , self.decorated.get ( x , y ) ) return self.cache.get ( x , y )                   クラスMaxDecorator : def __init__ ( self , decorated : Any , max : int ) - > None : self.decorated : Any = decorated self.max : int = max                  def get ( self , x : int , y : int ) - > None : self.decorated.get ( x , y ) > self.max場合: self.max返すself.decorated.get ( x , y )返す                 クラスMinDecorator : def __init__ ( self , decorated : Any , min : int ) - > None : self.decorated : Any = decorated self.min : int = min                  def get ( self , x : int , y : int ) - > int : self.decorated.get ( x , y ) < self.min場合: self.min返します。self.decorated.get ( x , y )返します                 クラスVisibilityDecorator : def __init__ ( self , decorated : Any ) -> None : self . decorated : Any = decorated            def get ( self , x : int , y : int ) -> int : return self . decorated . get ( x , y )           def draw ( self ) -> None : for y in range ( 10 ): for x in range ( 10 ): print ( " %3d " % self . get ( x , y ), end = ' ' ) print ()                 if  __name__  ==  "__main__" :  # 次に、デコレータのパイプラインを構築します。 random_square :  RandomSquare  =  RandomSquare ( 635 )  random_cache :  CacheDecorator  =  CacheDecorator ( random_square )  max_filtered :  MaxDecorator  =  MaxDecorator ( random_cache ,  200 )  min_filtered :  MinDecorator  =  MinDecorator ( max_filtered ,  100 )  final :  VisibilityDecorator  =  VisibilityDecorator ( min_filtered ) 最終.draw ( )

注記:

デコレータパターン(または上記の例のように、Pythonでこのデザインパターンを実装したもの)は、Pythonの言語機能であるPythonデコレータと混同しないでください。これらは異なるものです。

Python Wikiに次ぐ:

デコレータパターンは、『デザインパターンブック』で解説されているパターンです。これは、オブジェクトを同様のインターフェースを持つデコレートオブジェクトで囲むことで、その動作を見かけ上変更する方法です。これは、関数やクラスを動的に変更するための言語機能であるPythonのデコレータと混同しないように注意してください。[8]

結晶

抽象クラスCoffee抽象定義cost抽象定義ingredient終了        # シンプルなコーヒークラスの拡張SimpleCoffee < Coffee def cost 1 . 0 end        def材料"コーヒー"終了終了   # 抽象デコレータクラスCoffeeDecorator < Coffee protected getter decorated_coffee : Coffee         def初期化( @decorated_coffee )終了   def cost decorated_coffee . cost end    def材料装飾されたコーヒー.材料終了終了   クラスWithMilk < CoffeeDecorator defコストsuper + 0 . 5終了          def材料スーパー+ ", ミルク"終了終了     クラスWithSprinkles < CoffeeDecorator defコストsuper + 0 . 2終了          def材料super + ", スプリンクル" end end     クラスProgram def print ( coffee : Coffee ) puts "コスト: #{ coffee . cost } ; 材料: #{ coffee . ingredient } " end         def初期化coffee = SimpleCoffee . new print ( coffee )      コーヒー= WithMilk .新しい(コーヒー)印刷(コーヒー)    コーヒー= WithSprinkles . new (コーヒー) print (コーヒー) end end    プログラム新規

出力:

コスト: 1.0; 材料: コーヒーコスト: 1.5; 材料: コーヒー、ミルク費用: 1.7; 材料: コーヒー、牛乳、スプリンクル

C#

名前空間Wikipedia.Examples ; インターフェースIBike {文字列GetDetails ();ダブルGetPrice (); }     クラスAluminiumBike : IBike { public double GetPrice () => 100.0 ;         public string GetDetails () => "アルミバイク" ; }    クラスCarbonBike : IBike { public double GetPrice () => 1000.0 ;         パブリック文字列GetDetails () => "Carbon" ; }    抽象クラスBikeAccessories : IBike { private readonly IBike _bike ;         パブリックBikeAccessories ( IBikeバイク) { _bike =バイク; }        パブリック仮想ダブルGetPrice () => _bike.GetPrice ( ) ;      パブリック仮想文字列GetDetails () => _bike . GetDetails (); }     クラスSecurityPackage : BikeAccessories { public SecurityPackage ( IBike bike ) : base ( bike ) { // ... }            パブリックオーバーライド文字列GetDetails () => $"{base.GetDetails()} + セキュリティパッケージ" ;      パブリックオーバーライドdouble GetPrice () = > base.GetPrice ( ) + 1 ; }       クラスSportPackage : BikeAccessories { public SportPackage ( IBike bike ) : base ( bike ) { // ... }            public override string GetDetails () => $"{base.GetDetails()} + スポーツ パッケージ" ;      パブリックオーバーライドdouble GetPrice () = > base.GetPrice ( ) + 10 ; }       パブリッククラスBikeShop { public void UpgradeBike () { AluminiumBike basicBike = new AluminiumBike (); BikeAccessories upgraded = new SportPackage ( basicBike ); upgraded = new SecurityPackage ( upgraded );                     Console . WriteLine ( $"バイク: '{upgraded.GetDetails()}' 価格: {upgraded.GetPrice()}" ); }  静的void Main (文字列[]引数) { UpgradeBike (); } }      

出力:

バイク:「アルミバイク + スポーツパッケージ + セキュリティパッケージ」 価格:111

ルビー

class AbstractCoffee def print puts "コスト: #{ cost } ; 材料: #{ ingredient } " end end      クラスSimpleCoffee < AbstractCoffee def cost 1 . 0終了        def材料"コーヒー"終了終了   クラスWithMilk < SimpleDelegator def cost __getobj__ . cost + 0 . 5終了          def ingredient __getobj__ . ingredient + ", 牛乳" end end     クラスWithSprinkles < SimpleDelegator def cost __getobj__ . cost + 0 . 2終了          def ingredients __getobj__ . ingredients + ", Sprinkles" end end     コーヒー= SimpleCoffee .新しいコーヒー.印刷  コーヒー= WithMilk . new (コーヒー)コーヒー. print  コーヒー= WithSprinkles .新しい(コーヒー)コーヒー.印刷  


出力:

コスト: 1.0; 材料: コーヒーコスト: 1.5; 材料: コーヒー、ミルク費用: 1.7; 材料: コーヒー、牛乳、スプリンクル

参照

参考文献

  1. ^ ガンマ、エリック他 (1995). デザインパターン. マサチューセッツ州レディング: アディソン・ウェズリー出版. pp. 175ff. ISBN 0-201-63361-2
  2. ^ 「デコレータパターンの実装方法」。2015年7月7日時点のオリジナルよりアーカイブ。
  3. ^ 「デコレータパターン、使用をやめた理由、そして代替案」2022年3月8日。
  4. ^ エリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシデス (1994). デザインパターン:再利用可能なオブジェクト指向ソフトウェアの要素. アディソン・ウェズリー. pp. 175ff. ISBN 0-201-63361-2{{cite book}}: CS1 maint: multiple names: authors list (link)
  5. ^ abc 「デコレータデザインパターン - 問題、解決策、適用性」w3sDesign . 2023年4月8日時点のオリジナルよりアーカイブ。 2017年8月12日閲覧
  6. ^ フリーマン, エリック; フリーマン, エリザベス; シエラ, キャシー; ベイツ, バート (2004). ヘンドリクソン, マイク; ルーキデス, マイク (編). Head First Design Patterns (ペーパーバック) . 第1巻. オライリー. pp. 243, 252, 258, 260. ISBN 978-0-596-00712-6. 2012年7月2日閲覧
  7. ^ 「デコレータデザインパターン - 構造とコラボレーション」w3sDesign.com . 2017年8月12日閲覧
  8. ^ 「DecoratorPattern - Python Wiki」。wiki.python.org
  • Javaでのデコレータパターンの実装
  • ポートランドパターンリポジトリのデコレータパターンの説明
Retrieved from "https://en.wikipedia.org/w/index.php?title=Decorator_pattern&oldid=1322691637"