cover

你用過 ls -R 或 Windows 的 tree 指令嗎?不管是檔案還是資料夾,它們都能被「顯示」。資料夾裡面可能有更多資料夾和檔案,但對外的操作介面是一樣的。

這就是 Composite Pattern。

先講結論

Composite 把物件組成樹狀結構,讓「單一物件」和「物件組合」共用同一個介面。使用端不需要區分自己在操作的是一個葉子還是整棵子樹。

classDiagram
    class ProductCategory {
        <<abstract>>
        -name : String
        +display(indent)* void
    }
    class Product {
        -price : Number
        +display(indent) void
    }
    class Category {
        -children : ProductCategory[]
        +add(component) void
        +remove(component) void
        +display(indent) void
    }
    ProductCategory <|-- Product
    ProductCategory <|-- Category
    Category o-- ProductCategory : children

實戰:產品分類樹

Composite 模式:樹狀結構統一處理

class Product {
    constructor(name, price) {
        this.name = name;
        this.price = price;
    }
    display(indent = 0) {
        console.log(`${' '.repeat(indent)}- ${this.name}: $${this.price}`);
    }
}
 
class Category {
    constructor(name) {
        this.name = name;
        this.children = [];
    }
    add(component) { this.children.push(component); }
    remove(component) {
        const i = this.children.indexOf(component);
        if (i > -1) this.children.splice(i, 1);
    }
    display(indent = 0) {
        console.log(`${' '.repeat(indent)}+ ${this.name}`);
        this.children.forEach(child => child.display(indent + 2));
    }
}
 
const electronics = new Category('Electronics');
const phones = new Category('Phones');
 
phones.add(new Product('iPhone 15', 999));
phones.add(new Product('Samsung S24', 899));
 
electronics.add(phones);
electronics.add(new Product('MacBook Pro', 2499));
 
electronics.display();
// + Electronics
//   + Phones
//     - iPhone 15: $999
//     - Samsung S24: $899
//   - MacBook Pro: $2499

不管是 Product(葉子)還是 Category(容器),都有 display() 方法。呼叫端不用管它是什麼——直接 display() 就對了。

哪裡能看到 Composite?

  • React 元件樹:每個元件可以包含其他元件,render 的時候遞迴處理
  • DOM 結構div 裡面可以有 div,也可以有 span,但你對它們做 remove() 的方式是一樣的
  • 檔案系統:File 和 Directory 的操作介面一致

什麼時候不該用?

如果你的結構是扁平的(沒有巢狀),用陣列就好了。Composite 是為了處理「樹」的場景——沒有樹,就不需要 Composite。


Composite Pattern 就像俄羅斯套娃——打開一個裡面還有一個,但不管哪一層,長相都一樣。只是你永遠不知道裡面還有幾層


延伸閱讀