JavaにおけるDecoratorデザインパターンの例
デコレーターデザインパターンはオブジェクトの機能を実行時に変更するために使用されます。同じクラスの他のインスタンスには影響を与えず、個々のオブジェクトは変更された振る舞いを取得します。デコレーターデザインパターンは構造デザインパターンの一つであり(アダプターパターン、ブリッジパターン、コンポジットパターンなど)、抽象クラスやインターフェースを使用して実装されます。
デコレーターデザインパターン
オブジェクトの振る舞いを拡張するためには、継承やコンポジションを使用しますが、これはコンパイル時に行われ、クラスのすべてのインスタンスに適用されます。ランタイムでは新しい機能を追加したり既存の振る舞いを削除したりすることはできません – ここでDecoratorパターンが登場します。さまざまな種類の車を実装したい場合、Carというインターフェースを作成してアセンブルメソッドを定義し、Basic carを作成し、さらにSports carやLuxury carに拡張することができます。実装の階層は以下の画像のようになります。しかし、ランタイムでスポーツカーや高級車の両方の機能を備えた車を取得したい場合、実装は複雑になります。さらに、どの機能を最初に追加するかを指定する場合、さらに複雑になります。今、もし10種類の異なる車があると想像してみてください。継承とコンポジションを使用した実装ロジックは管理不可能になります。このようなプログラミングの状況を解決するために、Javaではデコレーターパターンを適用します。デコレーターデザインパターンを実装するには、以下のタイプが必要です。
-
- コンポーネントインターフェース – 実装されるメソッドを定義するインターフェースまたは抽象クラス。この場合、Carはコンポーネントインターフェースとして機能します。
-
- パッケージcom.scdev.design.decorator;
public interface Car {
public void assemble();
}
コンポーネントの実装 – コンポーネントインターフェースの基本的な実装。BasicCarクラスをコンポーネントの実装として使用することができます。
パッケージcom.scdev.design.decorator;
public class BasicCar implements Car {
@Override
public void assemble() {
System.out.print(“基本的な車。”);
}
}
デコレーター – デコレータークラスはコンポーネントインターフェースを実装し、コンポーネントインターフェースとのHAS-A関係を持ちます。コンポーネント変数は子デコレータークラスからアクセスできるようにする必要があるため、この変数を保護されたものにします。
パッケージcom.scdev.design.decorator;
public class CarDecorator implements Car {
protected Car car;
public CarDecorator(Car c){
this.car=c;
}
@Override
public void assemble() {
this.car.assemble();
}
}
具体的なデコレーター – ベースのデコレーター機能を拡張し、コンポーネントの動作を適宜変更する。LuxuryCarとSportsCarを具体的なデコレータークラスとして持つことができます。
パッケージcom.scdev.design.decorator;
public class SportsCar extends CarDecorator {
public SportsCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print(“スポーツカーの特徴を追加。”);
}
}
パッケージcom.scdev.design.decorator;
public class LuxuryCar extends CarDecorator {
public LuxuryCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print(“高級車の特徴を追加。”);
}
}
デコレーターデザインパターンのクラスダイアグラム
デコレーターデザインパターンのテストプログラム
package com.scdev.design.test;
import com.scdev.design.decorator.BasicCar;
import com.scdev.design.decorator.Car;
import com.scdev.design.decorator.LuxuryCar;
import com.scdev.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 design pattern is helpful in providing runtime modification abilities and hence more flexible. Its easy to maintain and extend when the number of choices are more.
- The disadvantage of decorator design pattern is that it uses a lot of similar kind of objects (decorators).
- Decorator pattern is used a lot in Java IO classes, such as FileReader, BufferedReader etc.