導入
コーヒーに「ミルクを追加」「砂糖を追加」「ホイップを追加」……こうしたオプションの組み合わせを、継承(MilkAndSugarCoffee のようなクラス)で全パターン分作るのは非現実的です。Decorator(デコレータ) は、同じインタフェースを実装した「包み紙」を何重にも重ねることで、機能を柔軟に追加する型です。
説明
DecoratorはAdapterと似た「ラップする」形をしていますが、目的が違います。Adapterは「インタフェースを変換する」ため、Decoratorは「同じインタフェースのまま機能を上乗せする」ためです。
public class Main {
public static void main(String[] args) {
Coffee coffee = new Milk(new Sugar(new SimpleCoffee()));
System.out.println(coffee.getDescription() + " " + coffee.getCost() + "円");
}
}
interface Coffee {
String getDescription();
int getCost();
}
class SimpleCoffee implements Coffee {
public String getDescription() {
return "コーヒー";
}
public int getCost() {
return 300;
}
}
abstract class CoffeeDecorator implements Coffee {
protected Coffee wrapped;
CoffeeDecorator(Coffee wrapped) {
this.wrapped = wrapped;
}
}
class Milk extends CoffeeDecorator {
Milk(Coffee wrapped) {
super(wrapped);
}
public String getDescription() {
return wrapped.getDescription() + " + ミルク";
}
public int getCost() {
return wrapped.getCost() + 50;
}
}
class Sugar extends CoffeeDecorator {
Sugar(Coffee wrapped) {
super(wrapped);
}
public String getDescription() {
return wrapped.getDescription() + " + 砂糖";
}
public int getCost() {
return wrapped.getCost() + 20;
}
}
classDiagram
Coffee <|.. SimpleCoffee
Coffee <|.. CoffeeDecorator
CoffeeDecorator <|-- Milk
CoffeeDecorator <|-- Sugar
CoffeeDecorator --> Coffee : wraps
class Coffee {
<<interface>>
+getDescription() String
+getCost() int
}
new Milk(new Sugar(new SimpleCoffee()))… いちばん内側のSimpleCoffee(素のコーヒー、300円)をSugarが包み、それをさらにMilkが包みます。CoffeeDecorator…Coffeeを実装しつつprotected Coffee wrapped;で「包んでいる相手」を保持する共通の抽象クラスです。MilkもSugarもこれを継承します。Milk.getCost()はwrapped.getCost() + 50と、内側の結果に自分の分を足して返すだけです。何重に包んでも同じ仕組みで金額が積み上がります(300 + 20 + 50 = 370円)。- 包む順番や個数を自由に変えられるのが継承だけの設計との違いです。「ミルクだけ」「砂糖だけ」「両方」も全部同じクラスの組み合わせで表現できます。
やってみよう
下のエディタを実行し、370円になることと、getDescription() が包んだ順に説明を積み上げていくことを確認しましょう。包む順序を new Sugar(new Milk(new SimpleCoffee())) に変えると説明文の順番がどう変わるか試してみてください。
演習
Whip という CoffeeDecorator を追加してください(+80円、説明に " + ホイップ" を追加)。main で new Whip(new Milk(new SimpleCoffee())) を作り、説明と金額を表示してください。
ヒント1を見る
Milk をまねて class Whip extends CoffeeDecorator { Whip(Coffee wrapped) { super(wrapped); } public String getDescription() { return wrapped.getDescription() + " + ホイップ"; } public int getCost() { return wrapped.getCost() + 80; } }。
ヒント2を見る
300(コーヒー) + 50(ミルク) + 80(ホイップ) = 430円になります。