這篇文章是我閱讀raywenderlich.com的Design 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 類型,分別是PDF和Music,并且遵循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不僅可以存儲PDF和Music,也可以存儲其他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。