1. 概要

デコレータパターンを使用して、静的または動的にオブジェクトに追加の責任を付加できます。デコレータは、元のオブジェクトへの拡張インターフェイスを提供します。

このパターンの実装では、継承よりも合成を優先します。これにより、装飾要素ごとにサブクラス化のオーバーヘッドを何度も減らすことができます。 この設計に関連する再帰は、必要な回数だけオブジェクトを装飾するために使用できます。

2. デコレータパターンの例

クリスマスツリーオブジェクトがあり、それを装飾したいとします。 装飾はオブジェクト自体を変更しません。 クリスマスツリーに加えて、ガーランド、ティンセル、ツリートッパー、バブルライトなどの装飾アイテムを追加しているだけです。

このシナリオでは、元のGang of Fourの設計と命名規則に従います。最初に、ChristmasTreeインターフェイスとその実装を作成します。

public interface ChristmasTree {
    String decorate();
}

このインターフェースの実装は次のようになります。

public class ChristmasTreeImpl implements ChristmasTree {

    @Override
    public String decorate() {
        return "Christmas tree";
    }
}

次に、このツリーの抽象TreeDecoratorクラスを作成します。 このデコレータは、 ChristmasTree インターフェイスを実装するだけでなく、同じオブジェクトを保持します。 同じインターフェイスから実装されたメソッドは、インターフェイスからdecorate()メソッドを呼び出すだけです。

public abstract class TreeDecorator implements ChristmasTree {
    private ChristmasTree tree;
    
    // standard constructors
    @Override
    public String decorate() {
        return tree.decorate();
    }
}

次に、装飾要素を作成します。 これらのデコレータは、抽象 TreeDecorator クラスを拡張し、要件に応じてdecorate()メソッドを変更します。

public class BubbleLights extends TreeDecorator {

    public BubbleLights(ChristmasTree tree) {
        super(tree);
    }
    
    public String decorate() {
        return super.decorate() + decorateWithBubbleLights();
    }
    
    private String decorateWithBubbleLights() {
        return " with Bubble Lights";
    }
}

この場合、次のことが当てはまります。

@Test
public void whenDecoratorsInjectedAtRuntime_thenConfigSuccess() {
    ChristmasTree tree1 = new Garland(new ChristmasTreeImpl());
    assertEquals(tree1.decorate(), 
      "Christmas tree with Garland");
     
    ChristmasTree tree2 = new BubbleLights(
      new Garland(new Garland(new ChristmasTreeImpl())));
    assertEquals(tree2.decorate(), 
      "Christmas tree with Garland with Garland with Bubble Lights");
}

最初のtree1オブジェクトでは、1つの Garland のみで装飾しているのに対し、他のtree2オブジェクトは1つので装飾していることに注意してください。 ]BubbleLightsと2つのGarlands。 このパターンにより、実行時に必要な数のデコレータを追加できる柔軟性が得られます。

4. 結論

この記事では、デコレータのデザインパターンを見てみました。 これは、次の場合に適しています。

  • オブジェクトの動作や状態を追加、拡張、または削除したい場合
  • クラスの単一のオブジェクトの機能を変更し、他のオブジェクトは変更しない場合

この例の完全なソースコードは、GitHubから入手できます。