【設(shè)計模式】19 - 組合模式 (Composite Pattern)

這篇文章是我閱讀raywenderlich.comDesign Patterns by Tutorials的總結(jié),文中的代碼是我閱讀書本之后根據(jù)自己的想法修改的。如果想看原版書籍,請點擊鏈接購買。


組合模式屬于結(jié)構(gòu)型模式,可以把多個對象整理成一個樹狀結(jié)構(gòu),把這些對象當(dāng)做一個對象來處理。涉及以下三種類型:

  • Component 協(xié)議:這個協(xié)議保證了可以使用同樣的方式來處理整棵樹中的對象
  • Leaf:樹結(jié)構(gòu)中的一個沒有子元素的組件
  • Composite:用來存儲 Leaf 對象和 Composite 的容器

所有的 Leaf 和 Composite 都遵循 Component 協(xié)議,所以我們可以在 Composite 中存儲不同類型的 Leaf 對象。例如,數(shù)組是一個 Composite,Component可以是 String、Int 等,也可以是一個數(shù)組。

什么時候使用

如果類的層級結(jié)構(gòu)形成了一個分支模式,我們分別創(chuàng)建分支和節(jié)點兩種類型來處理的話,這些類之間將會變得難以交互。這種情況下,我們可以使用組合模式,通過讓分支和節(jié)點遵循同一個協(xié)議,就可以把他們當(dāng)做一個對象來處理。Component 協(xié)議在這個模式中,起到一個抽象的層的作用,減少他們的復(fù)雜程度。

簡單demo

僅從上面的理論描述,是比較難理解這個模式的。這里以我們常見的電腦中的文件的層級關(guān)系來 demo 一下這個模式。

文件夾中,可以存儲某一類具體類型的文件,例如.pdf、.mp3等,還可以存儲文件夾。不管是具體的文件還是文件夾,在他們上面點擊右鍵有一些共同的操作,例如打開、刪除重命名等。我們可以在文件夾中存儲不同類型的文件和文件夾,就是因為他們都遵循了 Component 協(xié)議。

下面來看看代碼。

Component 協(xié)議

首先定義 Component 協(xié)議:

protocol File {
    var name: String { get set }
    func open()
}

定義了File協(xié)議,所有的 Leaf 和 Composite 對象都要遵循這個協(xié)議。

Leaf

final class PDF: File {
    var name: String
    init(name: String) {
        self.name = name
    }
    
    func open() {
        print("正在打開\(name)")
    }
}

final class Music: File {
    var name: String
    var artist: String
    
    init(name: String, artist: String) {
        self.name = name
        self.artist = artist
    }
    
    func open() {
        print("正在播放\(artist)的\(name)")
    }
}

定義了兩個 Leaf 類型,分別是PDFMusic,并且遵循File協(xié)議,各自實現(xiàn)了open()方法。

Composite

final class Folder: File {
    var name: String
    private(set) var files: [File] = []
    
    init(name: String) {
        self.name = name
    }
    
    func addFile(_ file: File) {
        files.append(file)
    }
    
    func open() {
        print("\n")
        print("正在顯示以下文件:")
        files.forEach { print("--- \($0.name)") }
    }
}

Folder屬于 Composite,實現(xiàn)了File協(xié)議,另外有一個數(shù)組可以存儲其他 File 類型,也就意味著 Folder不僅可以存儲PDFMusic,也可以存儲其他Folder對象。

使用

// 創(chuàng)建兩個文件夾
let desktop = Folder(name: "桌面")
let musicFolder = Folder(name: "我最愛的音樂")

// 創(chuàng)建具體的文件
let iOSDesignPattern = PDF(name: "iOS設(shè)計模式")

let diYiCi = Music(name: "第一次", artist: "光良")
let liXiang = Music(name: "理想", artist: "趙雷")

// 桌面文件夾添加音樂文件夾和 PDF 文件
desktop.addFile(musicFolder)
desktop.addFile(iOSDesignPattern)

// 把兩個音樂添加到音樂文件夾
musicFolder.addFile(diYiCi)
musicFolder.addFile(liXiang)

// 打開文件
iOSDesignPattern.open()
liXiang.open()

// 打開文件夾
desktop.open()
musicFolder.open()


// 結(jié)果
正在打開iOS設(shè)計模式
正在播放趙雷的理想

正在顯示以下文件:
--- 我最愛的音樂
--- iOS設(shè)計模式

正在顯示以下文件:
--- 第一次
--- 理想

從例子中可以看到,我們可以對不同的對象調(diào)用同一個方法,讓程序變得非常簡單。

總結(jié)

利用組合模式,我們可以對不同的對象以相同的方式處理。想象一下,如果沒有 Component 協(xié)議,要創(chuàng)建一個文件的容器會變得有多復(fù)雜。但是在使用組合模式之前,首先要保證應(yīng)用有分支結(jié)構(gòu)。

歡迎加入我管理的Swift開發(fā)群:536353151

下一篇文章:【設(shè)計模式】20 - 命令模式 (Command Pattern)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容