📢 この記事は gemini-2.5-flash によって翻訳されました
デコレーターパターン オブジェクト構造パターン
目的
オブジェクトに動的に追加の機能(責務)を与えられるよ。機能を追加するって点では、デコレーターパターンはサブクラスを作るよりずっと柔軟なんだ。
構造

登場するやつらはこんな感じ。
- Component: オブジェクトのインターフェースを定義するよ。このインターフェースを持つオブジェクトには、動的に機能を追加できるんだ。
- ConcreteComponent: 実際のオブジェクトを定義するやつ。これに機能を追加していくんだ。
- Decorator: Componentオブジェクトへのポインタを持っていて、Componentのインターフェースと一致するインターフェースを定義するんだ。
- ConcreteDecorator: コンポーネントに具体的な機能を追加する役目だよ。
いつ使うといいか
デコレーターパターンが役立つのは、こんな時かな。
- 他のオブジェクトに影響を与えずに、動的かつ透過的な方法で、個々のオブジェクトに機能を追加したいとき。
- 後から取り消せるような機能を扱いたいとき。
- サブクラスを作って拡張するのが難しいとき。たとえば、独立した拡張がたくさんあって、すべての組み合わせをサポートしようとするとサブクラスが爆発的に増えちゃう場合とか、クラス定義が隠されてたり、サブクラスを作るのに使えなかったりする場合だね。
例1
あるコーヒーショップでは、コーヒーを売るときにお客さんの要望に応じていろんなトッピングを追加できるんだ。で、追加されたトッピングに基づいて料金を計算するんだよ。コーヒーとトッピングの種類と値段は下の表のとおり。

この料金計算機能をデコレーターパターンで実装すると、こんなクラス図になるよ。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| import java.util.*;
abstract class Beverage { // 飲み物
String description = "Unknown Beverage";
public String getDescription() { return description; }
public abstract int cost();
}
abstract class CondimentDecorator extends Beverage { // トッピング
Beverage beverage;
}
class Espresso extends Beverage { // エスプレッソ
private final int ESPRESSO_PRICE = 25;
public Espresso() { description = "Espresso"; }
public int cost() { return ESPRESSO_PRICE; }
}
class DarkRoast extends Beverage { // ダークローストコーヒー
private final int DARKROAST_PRICE = 20;
public DarkRoast() { description = "DarkRoast"; }
public int cost() { rcturn DARKROAST PRICE; }
}
class Mocha extends CondimentDecorator { // モカ
private final int MOCHA_PRICE = 10;
public Mocha (Beverage beverage) { this.beverage = beverage; }
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}
public int cost() { return MOCHA_PRICE + beverage.cost(); }
}
class Whip extends CondimentDecorator { // ホイップ
private final int WHIP_PRICE = 8;
public Whip (Beverage beverage) { this.beverage = beverage; }
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public int cost() { return WHIP_PRICE + beverage.cost(); }
}
public class Coffee {
public static void main(String args[]) {
Beverage beverage = new DarkRoast();
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
System.out.println(beverage.getDescription() +" ¥"+ beverage.cost());
}
}
|
例2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| public class DecoratorPattern {
public static void main(String[] args) {
Person p = new Student("Mike");
p.Operation();
System.out.println("\n--------------------");
p = new DecoratorA(p);
p.Operation();
System.out.println("\n--------------------");
p = new DecoratorB(p);
p.Operation();
// No.2
Person p2 = new DecoratorB(new Student("Jane"));
}
}
abstract class Person{
protected String name;
public abstract void Operation();
}
class Student extends Person{
public Student(String name){
this.name = name;
}
@Override
public void Operation(){
System.out.print(name + " Study");
}
}
abstract class Decorator extends Person{
protected Person person;
}
class DecoratorA extends Decorator{
public DecoratorA(Person person){
this.person = person;
}
@Override
public void Operation() {
person.Operation();
System.out.print(" Play");
}
}
class DecoratorB extends Decorator{
public DecoratorB(Person person){
this.person = person;
}
@Override
public void Operation() {
person.Operation();
System.out.print(" Exam");
}
}
|