裝飾器模式

📢 本文由 gemini-2.5-flash 翻譯

Decorator Pattern 物件結構型模式

目的

動態地為一個物件新增一些額外的職責。就增加功能而言,Decorator 模式比產生子類別更具彈性。

結構

裝飾器

其中:

  • Component 定義一個物件介面,可以動態地為這些物件新增職責。
  • ConcreteComponent 定義一個物件,可以為這個物件新增一些職責。
  • Decorator 維持一個指向 Component 物件的指標,並定義一個與 Component 介面一致的介面。
  • ConcreteDecorator 向元件新增職責。

適用性

Decorator 模式適用於:

  • 在不影響其他物件的情況下,以動態、透明的方式為單一物件新增職責。
  • 處理那些可以撤銷的職責。
  • 當無法採用產生子類別的方式進行擴充時,一種情況是,可能有大量獨立的擴展,為支援每一種組合將產生大量的子類別,使得子類別數目呈爆炸性增長。另一種情況可能是,由於類別定義被隱藏,或類別定義不能用於產生子類別。

範例 1

某間咖啡店在販售咖啡時,可以根據顧客的要求在其中加入各種配料,咖啡店會根據所加入的配料來計算費用。咖啡店所供應的咖啡及配料種類和價格如下表所示。

裝飾器模式-例子-圖1

現採用裝飾器模式來實現計算費用的功能,得到如下類別圖。

裝飾器模式-例子-圖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
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() +" NT$ "+ 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 + " 學習");
    }
}

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(" 玩樂");
    }
}

class DecoratorB extends Decorator{
    public DecoratorB(Person person){
        this.person = person;
    }

    @Override
    public void Operation() {
        person.Operation();
        System.out.print(" 考試");
    }
}