JavaでのDecorator Design Patternの例

Decoratorデザインパターンは、オブジェクトの機能をランタイムで変更するために使用されます。同じクラスの他のインスタンスはこれに影響を受けませんので、個々のオブジェクトが変更された動作を取得します。Decoratorデザインパターンは、構造デザインパターン(アダプターパターンブリッジパターンコンポジットパターンなど)の1つであり、抽象クラスまたはインターフェースと合成を使用して実装されます。

Decoratorデザインパターン

継承またはコンポジションを使用して、オブジェクトの動作を拡張しますが、これはコンパイル時に行われ、クラスのすべてのインスタンスに適用されます。ランタイムで新しい機能を追加したり、既存の動作を削除したりすることはできません – これがDecoratorパターンが登場する場面です。異なる種類の車を実装したい場合、Carというインターフェースを作成してassembleメソッドを定義し、Basic carを作成し、さらにSports carやLuxury Carに拡張できます。実装の階層は以下の画像のようになります。しかし、実行時にスポーツカーとラグジュアリーカーの両方の機能を持つ車を取得したい場合、実装は複雑になり、さらにどの機能を最初に追加するかを指定したい場合は、さらに複雑になります。これに加えて、十種類の異なる車がある場合、継承とコンポジションを使用した実装ロジックは管理が不可能になります。この種のプログラミング状況を解決するために、JavaでDecoratorパターンを適用します。Decoratorデザインパターンを実装するには、次のタイプが必要です。

  1. コンポーネントインターフェース – 実装されるメソッドを定義するインターフェースまたは抽象クラス。この場合、Carがコンポーネントインターフェースになります。

    package com.journaldev.design.decorator;
    
    public interface Car {
    
    	public void assemble();
    }
    
  2. コンポーネントの実装 – コンポーネントインターフェースの基本実装。BasicCarクラスをコンポーネントの実装として持つことができます。

    package com.journaldev.design.decorator;
    
    public class BasicCar implements Car {
    
    	@Override
    	public void assemble() {
    		System.out.print("Basic Car.");
    	}
    
    }
    
  3. デコレーター – デコレータークラスはコンポーネントインターフェースを実装し、コンポーネントインターフェースとHAS-A関係を持ちます。コンポーネント変数は子デコレータークラスからアクセス可能である必要があるため、この変数をprotectedにします。

    package com.journaldev.design.decorator;
    
    public class CarDecorator implements Car {
    
    	protected Car car;
    	
    	public CarDecorator(Car c){
    		this.car=c;
    	}
    	
    	@Override
    	public void assemble() {
    		this.car.assemble();
    	}
    
    }
    
  4. 具象デコレーター – 基本のデコレーター機能を拡張し、コンポーネントの振る舞いを適宜変更します。具体的なデコレータークラスとしてLuxuryCarSportsCarを持つことができます。

    package com.journaldev.design.decorator;
    
    public class SportsCar extends CarDecorator {
    
    	public SportsCar(Car c) {
    		super(c);
    	}
    
    	@Override
    	public void assemble(){
    		super.assemble();
    		System.out.print(" スポーツカーの機能を追加します。");
    	}
    }
    
    package com.journaldev.design.decorator;
    
    public class LuxuryCar extends CarDecorator {
    
    	public LuxuryCar(Car c) {
    		super(c);
    	}
    	
    	@Override
    	public void assemble(){
    		super.assemble();
    		System.out.print(" ラグジュアリーカーの機能を追加します。");
    	}
    }
    

デコレーターデザインパターン – クラス図

デコレーターデザインパターン テストプログラム

package com.journaldev.design.test;

import com.journaldev.design.decorator.BasicCar;
import com.journaldev.design.decorator.Car;
import com.journaldev.design.decorator.LuxuryCar;
import com.journaldev.design.decorator.SportsCar;

public class DecoratorPatternTest {

	public static void main(String[] args) {
		Car sportsCar = new SportsCar(new BasicCar());
		sportsCar.assemble();
		System.out.println("\n*****");
		
		Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
		sportsLuxuryCar.assemble();
	}

}

クライアントプログラムがランタイムで異なる種類のオブジェクトを作成し、実行の順序を指定できることに注意してください。上記のテストプログラムの出力は次のとおりです:

Basic Car. Adding features of Sports Car.
*****
Basic Car. Adding features of Luxury Car. Adding features of Sports Car.

デコレーターデザインパターン – 重要なポイント

  • デコレーターデザインパターンは、ランタイムでの変更機能を提供するのに役立ち、より柔軟です。選択肢が多い場合には、維持および拡張が容易です。
  • デコレーターデザインパターンの欠点は、類似の種類のオブジェクト(デコレーター)を多く使用することです。
  • Decoratorパターンは、Java IOクラスでよく使用されており、FileReader、BufferedReaderなどがあります。

Source:
https://www.digitalocean.com/community/tutorials/decorator-design-pattern-in-java-example