iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 5
0

今天我們來介紹一個相當實用的 Pattern: Composite,這個 Pattern 在很多知名的框架都有被套用。它雖然看起來樸實,但是卻威力強大唷!話不多說,我們來了解 Composite 的內容吧!

產生的動機

在各類的繪圖軟體中,允許使用者可以畫單純的圖形外也能夠建立複雜的圖形,使用者也可透過把大量的單純圖性組成群組的方式來建造複雜的圖形。簡單要達成這樣的目的的實作就是定義這些圖形的物件,比如說:Line、Text 這樣的圖元物件,再加上一些物件像是這些圖元物件的容器。而這個時候就有一個問題產生了:程式碼必須要用不同的方式來對待對待圖元物件以及容器物件,而這樣就會讓整個應用程式的程式碼變得相當的複雜。Composite 的目的,就是要抽象化出一個抽象物件同時代表了圖元物件以及容器物件。

尚未套入 Composite 的繪圖軟體的 Class Diagram 會長這樣子:

若是使用 Compsite 的話,則會變成這樣子:

我們可以看到當使用了 Composite 之後,Client 就再也不需要去知道未來擴充的任何圖形物件的細節了!

結構與組成

  • Component
    1. 描述在 Composite 中物件的interface
    2. 實作一些共通的部分
    3. 定義存取、管理其 Child 物件的介面 (註:其實隨著時間的演進,這一項是否需要在 Component 定義引發了蠻多的討論,現在趨勢是傾向不在 Component 定義管理 Child 物件的介面。上面的 UML 保持當初 Gang of Four 所定義的,讀者也可以試著想想這個問題。)
  • Leaf (Primitive, Single, Part, Containee)
    1. 表示在 Composite 中 Leaf 物件,這樣的物件沒有 Child。
    2. 定義單一特定的物件行為
  • Composite (Group, Whole, Container, Branch)
    1. 定義有 Child 物件的行為
    2. 儲存 Child 元件
    3. 實作 Child 相關的物件行為
  • Client
    操縱 Compositition 物件透過 Component 所定義的 Interface

不囉唆,一句話!

Composite 讓 Client 可以用一致的方式看待個體和組成物件!

也就是說,Composite 這個 Pattern 的目的,就是讓 Client 不用在乎究竟所要處理的物件是一種個體物件或著是組成物件,針對其所定義的一致介面進行操作。

可以應用的情境

  1. 你想要表達物件的部分-整體的層次結構
  2. 你想要 Cleint 忽略掉個體物件及組成物件之間的差異:也就是希望 Client 用一致的方式去看待個體物件和組成物件

實作看看吧!

就用 PicCollage 中的一些角色來撰寫範例。我們有 Engneer, Designer, Coach(還有更多角色,但是為了保持範例簡單就先介紹這些)。Engineer 是工程師,Designer 是設計師,而 Coach 則負責為其所負責的某些人提供工作、職涯上的協助。而範例將用 Swift 來撰寫!不過因為 Swift 裡面並沒有 Abstract Class 的概念,我們可以用 Protocol 來實現 Composite。

protocol Employee: class {
    
    var name: String { get }
    
    func showInformation(indentCount: Int)
    
}

class Engineer: Employee {
    
    private(set) var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func showInformation(indentCount: Int) {
        let indent = String(repeating: "\t", count: indentCount)
        print("\(indent)Engineer: \(name)")
    }
    
    func develop() {
    
    }
    
}

class Designer: Employee {
    
    private(set) var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func showInformation(indentCount: Int) {
        let indent = String(repeating: "\t", count: indentCount)
        print("\(indent)Designer: \(name)")
    }
    
    func design() {
        
    }
    
}

class Coach: Employee {
    
    var name: String
    
    private var relations = [Employee]()
    
    init(name: String) {
        self.name = name
    }
    
    func showInformation(indentCount: Int) {
        let indent = String(repeating: "\t", count: indentCount)
        print("\(indent)Coach: \(name)")
        print("\(indent)Coaching: [")
        for relation in relations {
            relation.showInformation(indentCount: indentCount + 1)
        }
        print("\(indent)]")
    }
    
    func add(relation: Employee) {
        if !relations.contains(where: { $0 === relation}) {
            relations.append(relation)
        }
    }
    
    func remove(relation: Employee) {
        if let index = relations.firstIndex(where: {$0 === relation}) {
            relations.remove(at: index)
        }
    }
}


let russ = Coach(name: "Russ")
let chris = Coach(name: "Chris")
let ming = Designer(name: "Ming")
let yy = Coach(name: "YY")
let raymond = Engineer(name: "Raymond")
let simon = Engineer(name: "Simon")

russ.add(relation: yy)
yy.add(relation: raymond)
yy.add(relation: simon)
chris.add(relation: ming)

let employees: [Employee] = [russ, chris]
employees.forEach { (employee) in
    employee.showInformation(indentCount: 0)
    print("")
}

Output

Coach: Russ
Coaching: [
	Coach: YY
	Coaching: [
		Engineer: Raymond
		Engineer: Simon
	]
]

Coach: Chris
Coaching: [
	Designer: Ming
]

優點與缺點

其實 Composite 是一個很好的 Pattern,使用上並沒有太大的副作用。這邊還是做一下簡單的優缺點比較:

  • 優點
    1. 容易加入新類型的 Component
    2. 對 Client 來說就不需要知道他所處理到的是 Leaf 或著是 Composite
  • 缺點
    難以約束 Compoiste 中 Component 的種類數量

Reference

  1. Design Patterns - Elements of Reusabel Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides(Gang of four)
  2. https://www.youtube.com/watch?v=EXuaKKAlfqE
  3. https://medium.com/swiftcraft/swift-solutions-composite-pattern-fb3f08a30d08

作者:Charles


上一篇
[Design Pattern] Builder 建造者模式
下一篇
[Design Pattern] Decorator 裝飾者模式
系列文
什麼?又是/不只是 Design Patterns!?32

尚未有邦友留言

立即登入留言