オブジェクト指向プログラミングにおいて、デコレータパターンは、同じクラスの他のインスタンスの動作に影響を与えることなく、個々のオブジェクトに動的に動作を追加できるデザインパターンです。[ 1 ]デコレータパターンは、異なる関心を持つクラス間で機能を分散できるため、単一責任の原則に従うのに役立つことがよくあります。 [ 2 ]また、ソースコードを変更することなくクラスの機能を拡張できるため、オープン/クローズドの原則もサポートします。 [ 3 ]デコレータを使用すると、まったく新しいクラス階層を作成することなく、実行時にオブジェクトの動作を拡張または組み合わせることができるため、サブクラス化よりも柔軟で効率的になります。
デコレーター[ 4 ]デザインパターンは、23のGang-of-Fourデザインパターンの1つです。これらは、繰り返し発生する設計上の問題を解決し、柔軟で再利用可能なオブジェクト指向ソフトウェア、つまり実装、変更、テスト、再利用が容易なオブジェクトを設計する方法を説明しています。
デコレータパターンは、機能拡張のためのサブクラス化に代わる柔軟な方法を提供します。サブクラス化を使用する場合、異なるサブクラスがクラスを異なる方法で拡張します。しかし、拡張はコンパイル時にクラスにバインドされ、実行時に変更することはできません。デコレータパターンを使用すると、実行時にオブジェクトに責任を動的に追加(および削除)できます。これは、次のようなDecoratorオブジェクトを定義することによって実現されます。
Component)のインターフェースを透過的に実装し、すべてのリクエストをそれに転送します。これにより、さまざまなオブジェクトを操作してDecorator、実行時にオブジェクトの機能を動的に拡張することが可能になります。[ 5 ]

デコレータパターンは、設計時に一定の準備作業を行うことで、特定のオブジェクトの機能を静的に、あるいは場合によっては実行時に、同じクラスの他のインスタンスとは独立して拡張(装飾)するために使用できます。これは、元のクラスをラップする新しいデコレータクラスを設計することで実現されます。このラップは、以下の手順で実現できます。
このパターンは、複数のデコレータを重ねて使用でき、その都度オーバーライドされたメソッドに新しい機能を追加できるように設計されています。
デコレータと元のクラスオブジェクトは、共通の機能セットを共有していることに注意してください。前の図では、operation() メソッドは、デコレータ付きバージョンとデコレータなしバージョンの両方で使用可能でした。
装飾機能(メソッド、プロパティ、その他のメンバーなど)は通常、デコレータと装飾対象オブジェクトで共有されるインターフェース、ミックスイン(トレイトとも呼ばれる)、またはクラス継承によって定義されます。前の例では、クラスComponent はConcreteComponent とDecoratorから派生するサブクラスの両方に継承されています。
デコレータパターンは、サブクラス化の代替手段です。サブクラス化では、コンパイル時に新しいクラスを派生させることで追加の動作が導入され、このような変更は元のクラスのすべてのインスタンスに影響します。一方、デコレータパターンでは、同じクラスの他のインスタンスに影響を与えることなく、実行時に選択されたオブジェクトに新しい動作を動的に追加できます。この設計により、よりきめ細かく柔軟な動作拡張が可能になり、コードの再利用性と保守性が向上します。[ 5 ]
この違いは、機能を拡張する独立した方法が複数ある場合に最も重要になります。一部のオブジェクト指向プログラミング言語では、クラスをランタイム時に作成することはできず、設計時にどのような拡張機能の組み合わせが必要になるかを予測することは通常不可能です。これは、考えられるすべての組み合わせに対して新しいクラスを作成する必要があることを意味します。対照的に、デコレータはランタイム時に作成されるオブジェクトであり、使用ごとに組み合わせることができます。Javaと.NET Frameworkの両方の I/O ストリームの実装は、デコレータパターンを取り入れています。[ 5 ]

例えば、ウィンドウシステムにおけるウィンドウを考えてみましょう。ウィンドウの内容をスクロールできるようにするには、必要に応じて水平スクロールバーまたは垂直スクロールバーを追加したい場合があります。ウィンドウはWindowインターフェースのインスタンスで表され、このクラスにはスクロールバーを追加する機能がないと仮定します。スクロールバーを提供するサブクラスScrollingWindowを作成するか、既存のWindowオブジェクトにこの機能を追加するScrollingWindowDecoratorを作成することができます。現時点では、どちらの方法でも問題ありません。
さて、ウィンドウに境界線を追加する機能も必要だとしましょう。ここでも、元のWindowクラスにはこの機能がありません。ScrollingWindowサブクラスは、事実上新しい種類のウィンドウを作成しているため、問題となります。多くのウィンドウに境界線サポートを追加したいが、すべてではない場合、 WindowWithBorderやScrollingWindowWithBorderなどのサブクラスを作成する必要があります。この問題は、追加する新しい機能やウィンドウのサブタイプが増えるごとに悪化します。デコレータによる解決策としては、新しいBorderedWindowDecoratorが作成されます。ScrollingWindowDecoratorまたはBorderedWindowDecoratorの任意の組み合わせで、既存のウィンドウを装飾できます。すべてのウィンドウにこの機能を追加する必要がある場合は、基底クラスを変更できます。一方、場合によっては (たとえば、外部フレームワークを使用する場合)、基底クラスを変更することが不可能、合法、または不便な場合があります。
前の例では、SimpleWindowクラスとWindowDecoratorクラスはWindowインターフェイスを実装しており、このシナリオでウィンドウ コントロールを装飾するために必要なdraw()メソッドとgetDescription()メソッドを定義しています。
コマンド(ボタンの押下など)に応じてデコレータを追加または削除することは、一般的なUIパターンであり、コマンドデザインパターンと組み合わせて実装されることが多いです。たとえば、テキスト編集アプリケーションには、テキストをハイライト表示するボタンがあります。ボタンが押されると、現在選択されている個々のテキストグリフはすべて、draw()関数を変更するデコレータで囲まれ、ハイライト表示された状態で描画されます(実際の実装では、効率を最大化するために区切りシステムも使用されるでしょう)。
状態の変化に基づいてデコレータを適用または削除することも、よくあるユースケースです。状態のスコープに応じて、デコレータを一括で適用または削除できます。同様に、状態デザインパターンは、変化する機能をカプセル化するサブクラス化されたオブジェクトの代わりに、デコレータを使用して実装できます。このようにデコレータを使用することで、状態オブジェクトの内部状態と機能がより構成的になり、任意の複雑さを処理できるようになります。
デコレーションは、フライウェイト デザイン パターンでもよく使用されます。フライウェイト オブジェクトは、すべてのフライウェイト オブジェクトで共有される不変コンポーネントと、部分的に共有される場合も完全に共有されない場合もある、装飾されたバリアント コンポーネントの 2 つのコンポーネントに分割されます。フライウェイト オブジェクトをこのように分割することで、メモリ消費を削減できます。デコレータも通常はキャッシュされ、再利用されます。デコレータはすべて、共有される不変オブジェクトへの共通参照を持ちます。装飾された状態が部分的にバリアントである場合、デコレータもある程度共有できますが、使用中に状態を変更しないように注意する必要があります。iOS の UITableView は、この方法でフライウェイト パターンを実装しています。テーブルビューの再利用可能なセルは、共通のテーブルビュー行オブジェクトへの参照を持つデコレータであり、セルはキャッシュされ、再利用されます。
オブジェクトのコレクションに様々な方法でデコレータの組み合わせを適用すると、デコレータによって追加された機能を最大限に活用できるような方法でコレクションとインターフェースする際に、いくつかの問題が生じます。このような場合、アダプタパターンまたはビジターパターンの使用が有効です。アダプタパターンは、異なるインターフェースを共通の期待されるAPIに統合できます。同様に、ビジターパターンを使用すると、操作ロジックをオブジェクト自体に埋め込むことなく、装飾されたオブジェクトの構造全体にわたって操作を実行できます。複数のデコレータ層とインターフェースする場合は、さらに課題が生じるため、アダプタとビジターのロジックはそれを考慮して設計する必要があります。
デコレータは、機能拡張において、トップダウン型の階層的なアプローチではなく、構成的なアプローチをサポートします。デコレータを使用すると、実行時にインターフェースの動作を追加または変更できます。デコレータは、オブジェクトを多層構造で任意の組み合わせでラップするために使用できます。サブクラスで同じことを行うと、多重継承の複雑なネットワークを実装することになり、メモリ効率が悪く、ある時点からスケーラビリティが失われます。同様に、プロパティで同じ機能を実装しようとすると、オブジェクトの各インスタンスに不要なプロパティが肥大化します。上記のような理由から、デコレータはサブクラス化に代わるメモリ効率の良い方法としてよく考えられています。
デコレータは、サブクラス化できないオブジェクト、実行時に特性を変更する必要があるオブジェクト(他の箇所で述べたとおり)、または一般的に必要な機能が不足しているオブジェクトを特殊化するためにも使用できます。
デコレータパターンは、ファサードパターンを拡張することもできます。ファサードは、カプセル化する複雑なシステムと単純にインターフェースするように設計されていますが、システムに機能を追加するものではありません。しかし、複雑なシステムをラップすることで、システム内のサブコンポーネントの連携に基づいて新しい機能を導入できる余地が生まれます。たとえば、ファサードパターンは、複数の言語の辞書を1つの多言語辞書インターフェースに統合することができます。この新しいインターフェースは、言語間で単語を翻訳する新しい機能も提供できます。これはハイブリッドパターンであり、統合されたインターフェースが拡張のための余地を提供します。デコレータは、個々のオブジェクトをラップするだけでなく、このハイブリッドアプローチではオブジェクトのクラスターをラップすることもできると考えてください。
デコレータパターンの代替として、ラッパーが特定のインターフェースを尊重し、ポリモーフィックな動作をサポートする必要がある場合はアダプタを使用でき、基となるオブジェクトへのより簡単またはシンプルなインターフェースが必要な場合はファサードを使用できます。[ 6 ]

上記のUMLクラス図では、抽象クラスは装飾オブジェクト ( ) へのDecorator参照 ( ) を保持し、すべてのリクエストをそのオブジェクト ( ) に転送します。これにより、 は のクライアントに対して透過的 (不可視)になります。componentComponentcomponent.operation()DecoratorComponent
サブクラス(Decorator1、 )は、(リクエストを転送する前/後に) に追加されるべきDecorator2追加の動作()を実装します。 シーケンス図は、実行時の相互作用を示しています。オブジェクトは、オブジェクトとオブジェクトを介して動作し、オブジェクトの機能を拡張します。 は、リクエストをに転送するを呼び出します。は 、リクエストをに転送した後でを実行し、に返します 。はを実行し 、に返します。addBehavior()ComponentClientDecorator1Decorator2Component1Clientoperation()Decorator1Decorator2Decorator2addBehavior()Component1Decorator1addBehavior()Client
この実装(C++23の機能を使用)は、本書に掲載されているC++98以前の実装に基づいています。
import 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 ( "コーヒーを飲んでいます" ); } };class Soda : public Beverage { public : virtual void drink () override { std :: print ( "Drinking Soda" ); } };class BeverageDecorator : public Beverage { private : unique_ptr < Beverage > component ; protected : void callComponentDrink () { if ( component ) { component -> drink (); } } public : BeverageDecorator () = delete ;explicit BeverageDecorator ( unique_ptr < Beverage > component ) : component { std :: move ( component )} {} virtual void drink () = 0 ; };class 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 ( ", 濃厚なミルク入り {}%" , percentage ); } };class 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 ); } };class 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 );コーヒー->ドリンク();return 0 ; }プログラムの出力は次のようになります。
炭酸飲料(氷3個、砂糖小さじ1杯入り)コーヒー(氷16個、濃厚ミルク3 %、砂糖小さじ2杯入り)完全な例は、godbolt のページでテストできます。
ここでは2つの選択肢が提示されています。1つ目は、動的で実行時に構成可能なデコレータ(明示的にプロキシしない限り、デコレートされた関数を呼び出す際に問題が発生する)であり、2つ目は、ミックスイン継承を使用するデコレータです。
import std ;std :: stringを使用します。class Shape { public : virtual ~ Shape () = default ; virtual string getName () const = 0 ; };class 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 ); } };class 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 ( "{} which is colored {}" , shape . getName (), color ); } };int main () { Circle circle ; ColoredShape coloredShape { "red" , circle }; std :: println ( " { }" , coloredShape.getName ( )); }import std ;using std :: string ; using std :: unique_ptr ;class WebPage { public : virtual void display () = 0 ; virtual ~ WebPage () = default ; };class BasicWebPage : public WebPage { private : string html ; public : void display () override { std :: println ( "Basic WEB page" ); } };class WebPageDecorator : public WebPage { private : unique_ptr <WebPage> webPage ; public : explicit WebPageDecorator ( unique_ptr <WebPage> webPage ) : webPage { std :: move ( webPage ) } { }void display () override { webPage -> display (); } };class AuthenticatedWebPage : public WebPageDecorator { public : explicit AuthenticatedWebPage ( unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {}void authenticateUser () { std :: println ( "認証完了" ); }void display () override { authenticateUser (); WebPageDecorator :: display (); } };class AuthorizedWebPage : public WebPageDecorator { public : explicit AuthorizedWebPage ( unique_ptr < WebPage > webPage ) : WebPageDecorator ( std :: move ( webPage )) {}void authorizedUser () { std :: println ( "認証完了" ); }void display () override { 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++のテンプレート引数からの継承機能によって可能になった、静的なデコレータの実装を示しています。
import std ;std :: stringを使用します。class Circle { private : float radius = 10.0f ; public : void resize ( float factor ) noexcept { radius *= factor ; }[[ nodiscard ]] string getName () const { return std :: format ( "半径 {} の円" , radius ); } };template < typename T > class ColoredShape : public T { private : string color ; public : explicit ColoredShape ( const string & color ) : color { color } {} [[ nodiscard ]] string getName () const { return std :: format ( "{} which is colored {}" , 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 "シンプルなウィンドウ" ; } }Window以下のクラスには、デコレータクラス自体を含め、すべてのクラスのデコレータが含まれています。
// 抽象デコレータクラス - Window を実装していることに注意してくださいabstract class WindowDecorator implements Window { private final Window windowToBeDecorated ; // 装飾される Windowpublic WindowDecorator ( Window 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 HorizontalScrollBarDecorator extends WindowDecorator { public Horizo ntalScrollBarDecorator ( Window windowToBeDecorated ) { super ( windowToBeDecorated ); }@Override public void draw () { super.draw (); drawHorizontalScrollBar ( ) ; }private void drawHorizontalScrollBar () { // 水平スクロールバーを描画する}@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テストクラスです。
import org.junit.Assert ; import org.junit.Test ;public class WindowDecoratorTest { @Test public void testWindowDecoratorTest () { Window decoratedWindow = new Horizo ntalScrollBarDecorator ( new VerticalScrollBarDecorator ( new SimpleWindow ()) ); // 説明に水平スクロールバーと垂直スクロールバーが含まれていることを検証しますAssert . assertEquals ( "シンプルなウィンドウ、垂直スクロールバー、水平スクロールバーを含む" , decoratedWindow . getDescription ()); } }次の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 ;public 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 ( ) ); } }以下は、ミルクとトッピングで完全に装飾されたコーヒーインスタンスを作成し、コーヒーのコストを計算して材料を出力するテストプログラムです。
public class Main { public static void printInfo ( Coffee c ) { System . out . printf ( "コスト: %s; 材料: %s%n" , c . getCost (), c . getIngredients ()); }public static 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;材料:コーヒー、牛乳、トッピング
abstract class Component { protected $data ; protected $value ;抽象パブリック関数getData ();抽象パブリック関数getValue (); }class ConcreteComponent extends Component { public function __construct () { $this -> value = 1000 ; $this -> data = "Concrete Component: \t { $this -> value } \n " ; }public function getData () { return $this- > data ; }public function getValue () { return $this- > value ; } }抽象クラスDecorator はComponentを継承します{}class ConcreteDecorator1 extends Decorator { public function __construct ( Component $data ) { $this -> value = 500 ; $this -> data = $data ; }public function getData () { return $this -> data -> getData () . "Concrete Decorator 1: \t { $this -> value } \n " ; }public function getValue () { return $this- > value + $this- > data- > getValue (); } }class ConcreteDecorator2 extends Decorator { public function __construct ( Component $data ) { $this -> value = 500 ; $this -> data = $data ; }public function getData () { return $this -> data -> getData () . "Concrete Decorator 2: \t { $this -> value } \n " ; }public function getValue () { return $this- > value + $this- > data- > getValue (); } }class Client { private $component ;public function __construct () { $this -> component = new ConcreteComponent (); $this -> component = $this -> wrapComponent ( $this -> component );echo $this- > component- > getData (); echo "クライアント: \t\t\t " ; echo $this- > component- > getValue (); }private function wrapComponent ( Component $component ) { $component1 = new ConcreteDecorator1 ( $component ); $component2 = new ConcreteDecorator2 ( $component1 ); return $component2 ; } }$client = new Client ();// 結果: #quanton81//コンクリートコンポーネント: 1000 //コンクリートデコレーター1: 500 //コンクリートデコレーター2: 500 //クライアント: 2000Python Wikiの「DecoratorPattern」から引用した以下のPythonの例は、デコレータをパイプライン化してオブジェクトに動的に多くの動作を追加する方法を示しています。
""" 0~255の値を持つ10×10のグリッドの世界で、デコレーターを実演しました。"""import random from typing import Anydef s32_to_u16 ( x : int ) -> int : sign : int if x < 0 : sign = 0xF000 else : sign = 0 bottom : int = x & 0x00007FFF return bottom | signdef seed_from_xy ( x : int , y : int ) -> int : return s32_to_u16 ( x ) | ( s32_to_u16 ( y ) << 16 )class RandomSquare : def __init __ ( self , seed_modifier : int ) - > None : self.seed_modifier : int = seed_modifierdef 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 )class DataSquare : def __init __ ( self , initial_value : int = None ) - > None : self.data : list [ int ] = [ initial_value ] * 10 * 10def 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 ] = uclass 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 )class MaxDecorator : def __init __ ( self , decorated : Any , max : int ) - > None : self.decorated : Any = decorated self.max : int = maxdef get ( self , x : int , y : int ) -> None : if self . decorated . get ( x , y ) > self . max : return self . max return self . decorated . get ( x , y )class MinDecorator : def __init __ ( self , decorated : Any , min : int ) - > None : self.decorated : Any = decorated self.min : int = mindef get ( self , x : int , y : int ) -> int : if self . decorated . get ( x , y ) < self . min : return self . min return self . decorated . get ( x , y )class VisibilityDecorator : def __init__ ( self , decorated : Any ) -> None : self . decorated : Any = decorateddef 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 )final.draw ( )注記:
デコレーターパターン(または上記の例のような、このデザインパターンをPythonで実装したもの)は、Pythonの言語機能であるPythonデコレーターと混同してはいけません。これらは別物です。
Python Wikiに次いで:
デコレーターパターンは、『デザインパターンブック』で説明されているパターンです。これは、類似したインターフェースを持つ装飾オブジェクトでオブジェクトを囲むことで、オブジェクトの振る舞いを見かけ上変更する方法です。これは、関数やクラスを動的に変更するための言語機能であるPythonのデコレーターと混同しないでください。[ 8 ]
抽象クラスCoffee抽象定義cost抽象定義ingredients end# シンプルなコーヒークラスの拡張SimpleCoffee < Coffee def cost 1 . 0 enddef ingredients "コーヒー" end end# 抽象デコレータクラスCoffeeDecorator < Coffee protected getter decorated_coffee : Coffeedef initialize ( @decorated_coffee ) enddef cost decorated_coffee.cost enddef ingredients decorated_coffee . ingredients end endclass WithMilk < CoffeeDecorator def cost super + 0 . 5 enddef ingredients super + ", Milk" end endclass WithSprinkles < CoffeeDecorator def cost super + 0 . 2 enddef ingredients super + ", Sprinkles" end endclass Program def print ( coffee : Coffee ) puts "価格: #{ coffee . cost } ; 材料: #{ coffee . ingredients } " enddef initialize coffee = SimpleCoffee.new print ( coffee )coffee = WithMilk.new ( coffee ) print ( coffee )coffee = WithSprinkles.new ( coffee ) print ( coffee ) end endプログラム.新しい出力:
価格:1.0;材料:コーヒー 価格:1.5;材料:コーヒー、牛乳 価格:1.7;材料:コーヒー、牛乳、トッピング
名前空間Wikipedia.Examples ;interface IBike { string GetDetails (); double GetPrice (); }class AluminiumBike : IBike { public double GetPrice () => 100.0 ;public string GetDetails () => "アルミ製自転車" ; }class CarbonBike : IBike { public double GetPrice () => 1000.0 ;public string GetDetails () => "Carbon" ; }abstract class BikeAccessories : IBike { private readonly IBike _bike ;public BikeAccessories ( IBike bike ) { _bike = bike ; }public virtual double GetPrice () => _bike.GetPrice ( ) ;public virtual string GetDetails ( ) => _bike.GetDetails ( ); }class SecurityPackage : BikeAccessories { public SecurityPackage ( IBike bike ) : base ( bike ) { // ... }public override string GetDetails () => $"{base.GetDetails()} + セキュリティパッケージ" ;public override double GetPrice () = > base.GetPrice ( ) + 1 ; }class SportPackage : BikeAccessories { public SportPackage ( IBike bike ) : base ( bike ) { // ... }public override string GetDetails () => $"{base.GetDetails()} + スポーツパッケージ" ;public override double GetPrice () = > base.GetPrice ( ) + 10 ; }public class BikeShop { public void UpgradeBike () { AluminiumBike basicBike = new AluminiumBike (); BikeAccessories upgraded = new SportPackage ( basicBike ); upgraded = new SecurityPackage ( upgraded );Console.WriteLine ( $"自転車: '{upgraded.GetDetails()}' 費用: {upgraded.GetPrice()} " ) ; }static void Main ( string [] args ) { UpgradeBike (); } }出力:
自転車:「アルミ製自転車+スポーツパッケージ+セキュリティパッケージ」価格:111
class AbstractCoffee def print puts "コスト: #{ cost } ; 材料: #{ ingredients } " end endclass SimpleCoffee < AbstractCoffee def cost 1 . 0 enddef ingredients "コーヒー" end endclass WithMilk < SimpleDelegator def cost __getobj__ . cost + 0 . 5 enddef ingredients __getobj__ . ingredients + ", Milk" end endclass WithSprinkles < SimpleDelegator def cost __getobj__ . cost + 0 . 2 enddef ingredients __getobj__ . ingredients + ", Sprinkles" end endcoffee = SimpleCoffee.new coffee.printcoffee = WithMilk.new ( coffee ) coffee.printcoffee = WithSprinkles.new ( coffee ) coffee.print出力:
価格:1.0;材料:コーヒー 価格:1.5;材料:コーヒー、牛乳 価格:1.7;材料:コーヒー、牛乳、トッピング
{{cite book}}: CS1 maint: 複数の名前: 著者リスト (リンク)