做者:葉@毛豆前端javascript
組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構,組合模式使得用戶對單個對象和組合對象的使用具備一致性前端
在定義中提到了「部分-總體」、「單個對象」、「組合對象」這幾個關鍵詞,所以掌握組合模式的重點是要理解清楚 「部分/總體」 還有 」單個對象「 與 "組合對象" 的含義java
組合模式的做用即定義描述的那樣,有兩個做用:git
針對以上的兩點,下面作一個詳細說明,首先看一段代碼:這是一個模擬電腦開機,運行應用程序的簡單宏命令例子github
let runWeChat = {
run : function() {
console.log("wechat已經啓動")
}
}
let runQQ = {
run : function() {
console.log("QQ已經啓動")
}
}
let runChrome = {
run : function() {
console.log("Google Chrome已經啓動")
}
}
let applicationCommand = function () {
return {
commandLists : [],
addCommand: function (command) {
this.commandLists.push(command)
},
run: function () {
this.commandLists.map((item, index) => {
item.run()
})
}
}
}
let ac = applicationCommand ()
ac.addCommand(runWeChat)
ac.addCommand(runQQ)
ac.addCommand(runChrome)
ac.run()
複製代碼
這樣的方式在實際的開發中能夠帶來很大的便利性。當咱們在開機程序中添加命令時,無需關心這個是宏命令仍是子命令,只關心這個命令是有能夠執行的run方法,那麼這個命令就能夠被添加。執行的差別是在代碼裏體現的,對客戶無感。瀏覽器
仍以上面的宏命令爲例,請求在樹最頂端的對象往下傳遞,若是此時處理請求的是普通子命令(葉子對象),這個時候葉子對象本身就會對請求作出處理;若此時處理請求的宏命令(組合對象),組合對象則遍歷它的子對象,將請求傳遞下去。上面的例子只是簡單的組合對象下只有葉子對象,葉子對象是樹的最小單位,不會再有其餘子節點,組合對象下可能還有子節點,以下圖:微信
請求是由上至下沿着樹進行傳遞,直到葉子對象,做爲使用者,只須要關心樹最頂層的組合對象,只要請求這個組合對象,請求便會沿着樹往下傳遞,順次到達全部的葉子對象app
在公司裏,必然存在員工的部門以及從屬關係(這裏只考慮每一個人只屬於一個部門),公司分爲衆多的部門,每一個部門會有一個主管,帶領下面的員工,這個員工可能只是最末端的一個職位,也有可能在他手下還有一波兄弟,每一個部門是一個組合對象,部門下面的小組仍是一個組合對象,這樣就給構成了組合的模式,接下來咱們來看下例子:性能
let leaderStaff = function (name, sex, apartment) {
this.name = name;
this.sex = sex;
this.apartment = apartment;
this.children = [];
}
leaderStaff.prototype.add = function (child) {
this.children.push(child)
}
leaderStaff.prototype.doCount = function () {
console.log("leader", this.name +"--"+ this.sex +"--"+ this.apartment)
this.children.map((child, index) => {
child.doCount()
})
}
let staff = function(name, sex, apartment) {
this.name = name;
this.sex = sex;
this.apartment = apartment;
}
staff.prototype.add = function() {
throw new Error("我還只是個孩子!!->_->")
}
staff.prototype.doCount = function () {
console.log("普通員工",this.name +"--"+ this.sex +"--"+ this.apartment)
}
let leaderStaff1 = new leaderStaff('大旺', '男', 'A')
let leaderStaff2 = new leaderStaff('大張', '男', 'B')
let leaderStaff3 = new leaderStaff('大李', '男', 'C')
let leaderStaff4 = new leaderStaff('大無', '女', 'D')
let staff5 = new staff('小馬', '', '0')
let staff1 = new staff('小黑', '男', 'd')
let staff2 = new staff('小撒', '女', 'e')
let staff3 = new staff('小周', '女', 'f')
let staff4 = new staff('小鄭', '男', 'g')
leaderStaff1.add(leaderStaff3);
leaderStaff1.add(leaderStaff2);
leaderStaff1.add(leaderStaff4)
leaderStaff1.add(staff1)
leaderStaff3.add(staff4)
leaderStaff3.add(staff3)
leaderStaff4.add(staff2)
// staff3.add(staff5)
leaderStaff1.doCount()
複製代碼
執行結果如圖:ui
這樣看起來彷佛樹形結構不是很明顯,因此咱們再看下中間執行的數據:
這裏咱們把擁有下級(子元素)的都統稱爲老闆(leaderStaff),沒有下級(子元素)的都叫作員工(staff),員工下面在沒有子元素,不然拋出錯誤。他們都有doCount這個方法。很明顯leaderStaff1(大旺)有四個子元素,其中大李又有兩個子元素,以及大無有一個子元素,這些都是被稱做組合對象,使用的人也不須要分辨他是孩子對象仍是祖先元素。這裏咱們改變樹的結構,增長新的數據,卻不用修改原來的代碼,這是可取的,符合開放-封閉原則
咱們如今瞭解了組合模式,還要說下在使用組合模式的實收,有一些值得咱們注意的地方:
組合模式不是父子關係 組合模式的樹形結構容易讓人誤覺得組合對象和葉子對象之間是父子關係,但並非這樣的。組合模式是一種HAS-A(聚合)的關係,而不是IS-A,組合對象把請求委託給它所包的全部葉對象,它們可以合做的關鍵是擁有相同是的接口
對葉對象操做的一致性 組合模式除了要求組合對象和也對象擁有相同的接口以外,還有一個必要條件,就是對一組也對象的操做必須具備一致性
雙向映射關係 上面的例子中,公司給員工發送通知的步驟是從公司到各個部分,再到小組,最後纔到每一個員工的郵箱裏,這即是一個組合模式的例子,可是存在特殊狀況,一個員工同屬於多個部門的時候就沒有了嚴格意義上的層次結構,在這種狀況下便再也不適用組合模式,不然該員工極可能收到兩份郵件
用職責鏈模式提升組合模式性能 一些狀況下,生成樹的結構十分複雜,節點數量衆多,在遍歷的過程當中,消耗性能。職責鏈模式就是在遍歷的過程當中,只讓請求順着聯調從父元素往子元素上傳遞,或子元素往父元素身上船體,知道遇到能處理請求的對象未知,避免浪費
雖然多數時候組,合模式帶給咱們便利,讓咱們可使用樹形的方式去建立對象結構,還能夠忽略組合對象和單個對象之間的差異,使用一致的方式處理。可是,它的缺點也須要注意:他們的區別只有在運行的時候纔會體現出來。