我們平時(shí)開(kāi)發(fā)過(guò)程中,一定會(huì)遇到這種情況:同時(shí)處理簡(jiǎn)單對(duì)象和由簡(jiǎn)單對(duì)象組成的復(fù)雜對(duì)象,這些簡(jiǎn)單對(duì)象和復(fù)雜對(duì)象會(huì)組合成樹(shù)形結(jié)構(gòu),在客戶端對(duì)其處理的時(shí)候要保持一致性。比如電商網(wǎng)站中的產(chǎn)品訂單,每一張產(chǎn)品訂單可能有多個(gè)子訂單組合,比如操作系統(tǒng)的文件夾,每個(gè)文件夾有多個(gè)子文件夾或文件,我們作為用戶對(duì)其進(jìn)行復(fù)制,刪除等操作時(shí),不管是文件夾還是文件,對(duì)我們操作者來(lái)說(shuō)是一樣的。在這種場(chǎng)景下,就非常適合使用組合模式來(lái)實(shí)現(xiàn)。
基本知識(shí)
組合模式:將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu),組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
組合模式主要有三個(gè)角色:
(1)抽象組件(Component):抽象類(lèi),主要定義了參與組合的對(duì)象的公共接口
(2)子對(duì)象(Leaf):組成組合對(duì)象的最基本對(duì)象
(3)組合對(duì)象(Composite):由子對(duì)象組合起來(lái)的復(fù)雜對(duì)象
理解組合模式的關(guān)鍵是要理解組合模式對(duì)單個(gè)對(duì)象和組合對(duì)象使用的一致性,我們接下來(lái)說(shuō)說(shuō)組合模式的實(shí)現(xiàn)加深理解。
組合模式算是為在頁(yè)面動(dòng)態(tài)創(chuàng)建UI量身定做的,你可以只使用一條命=命令為許多對(duì)象初始化一些復(fù)雜的或者遞歸的操作.組合模式提供了兩個(gè)有點(diǎn):
(1)允許你將一組對(duì)象當(dāng)成特定的對(duì)象.組合對(duì)象(A composite)和組成它的子對(duì)象實(shí)現(xiàn)相同的操作.對(duì)組合對(duì)象執(zhí)行某一個(gè)操作將會(huì)使該對(duì)象下的所有子對(duì)象執(zhí)行相同的操作.因此你不僅可以無(wú)縫的替換單個(gè)對(duì)象為一組對(duì)象集合,反過(guò)來(lái)也一樣.這些獨(dú)立的對(duì)象之間即所謂松散耦合的.
(2)組合模式會(huì)將子對(duì)象集組合成樹(shù)結(jié)構(gòu)并且允許遍歷整個(gè)樹(shù).這樣可以隱藏內(nèi)部實(shí)現(xiàn)并且允許你以任意的方式組織子對(duì)象.這個(gè)對(duì)象(組合對(duì)象)的任何代碼將不會(huì)依賴內(nèi)部子對(duì)象的實(shí)現(xiàn).
組合模式的實(shí)現(xiàn)
(1)最簡(jiǎn)單的組合模式
HTML文檔的DOM結(jié)構(gòu)就是天生的樹(shù)形結(jié)構(gòu),最基本的元素醉成DOM樹(shù),最終形成DOM文檔,非常適用適用組合模式。
我們常用的jQuery類(lèi)庫(kù),其中組合模式的應(yīng)用更是頻繁,例如經(jīng)常有下列代碼實(shí)現(xiàn):
?
1
$(".test").addClass("noTest").remove("test");
這句簡(jiǎn)單的代碼就是獲取class包含test的元素,然后進(jìn)行addClass和removeClass處理,其中不論$(“.test”)是一個(gè)元素,還是多個(gè)元素,最終都是通過(guò)統(tǒng)一的addClass和removeClass接口進(jìn)行調(diào)用。
我們簡(jiǎn)單模擬一下addClass的實(shí)現(xiàn):
var addClass = function (eles, className) {
if (eles instanceof NodeList) {
for (var i = 0, length = eles.length; i < length; i++) {
eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' '));
}
}
else if (eles instanceof Node) {
eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
}
else {
throw "eles is not a html node";
}
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");
這段代碼簡(jiǎn)單的模擬了addClass的實(shí)現(xiàn)(暫不考慮兼容性和通用性),很簡(jiǎn)單地先判斷節(jié)點(diǎn)類(lèi)型,然后根據(jù)不同類(lèi)型添加className。對(duì)于NodeList或者是Node來(lái)說(shuō),客戶端調(diào)用都是同樣的使用了addClass這個(gè)接口,這個(gè)就是組合模式的最基本的思想,使部分和整體的使用具有一致性。
(2)典型的例子
前面我們提到一個(gè)典型的例子:產(chǎn)品訂單包含多個(gè)產(chǎn)品子訂單,多個(gè)產(chǎn)品子訂單組成一個(gè)復(fù)雜的產(chǎn)品訂單。由于Javascript語(yǔ)言的特性,我們將組合模式的三個(gè)角色簡(jiǎn)化成2個(gè)角色:
(1)子對(duì)象:在這個(gè)例子中,子對(duì)象就是產(chǎn)品子訂單
(2)組合對(duì)象:這里就是產(chǎn)品的總訂單
假設(shè)我們開(kāi)發(fā)一個(gè)旅游產(chǎn)品網(wǎng)站,其中包含機(jī)票和酒店兩種子產(chǎn)品,我們定義了子對(duì)象如下:
function FlightOrder() { }
FlightOrder.prototyp.create = function () {
console.log("flight order created");
}
function HotelOrder() { }
HotelOrder.prototype.create = function () {
console.log("hotel order created");
}
上面的代碼定義了兩個(gè)類(lèi):機(jī)票訂單類(lèi)和酒店訂單類(lèi),每個(gè)類(lèi)都有各自的訂單創(chuàng)建方法。
接下來(lái)我們創(chuàng)建一個(gè)總訂單類(lèi):
function TotalOrders() {
this.orderList = [];
}
TotalOrders.prototype.addOrder = function (order) {
this.orderList.push(order);
}
TotalOrders.prototype.create = function (order) {
for (var i = 0, length = this.orderList.length; i < length; i++) {
this.orderList[i].create();
}
}
這個(gè)對(duì)象主要有3個(gè)成員:訂單列表,添加訂單的方法,創(chuàng)建訂單的方法。
在客戶端使用的時(shí)候如下:
var flight = new FlightOrder();
flight.create();
var orders = new TotalOrders();
orders.addOrder(new FlightOrder());
orders.addOrder(new HotelOrder());
orders.create();
客戶端調(diào)用展示了兩種方式,一種是單一的創(chuàng)建機(jī)票訂單,一種是創(chuàng)建多張訂單,但最終都是通過(guò)create方法進(jìn)行創(chuàng)建,這就是一個(gè)很典型的組合模式的應(yīng)用場(chǎng)景。
總結(jié)
組合模式并不難理解,它主要解決的是單一對(duì)象和組合對(duì)象在使用方式上的一致性問(wèn)題。如果對(duì)象具有明顯的層次結(jié)構(gòu)并且想要統(tǒng)一地使用它們,這就非常適合使用組合模式。在Web開(kāi)發(fā)中,這種層次結(jié)構(gòu)非常常見(jiàn),很適合使用組合模式,尤其是對(duì)于JS來(lái)說(shuō),不用拘泥于傳統(tǒng)面向?qū)ο笳Z(yǔ)言的形式,靈活地利用JS語(yǔ)言的特性,達(dá)到對(duì)部分和整體使用的一致性。
(1)使用組合模式的場(chǎng)景
在遇到下面兩種情況的時(shí)候才使用組合模式
A.含有某種層級(jí)結(jié)構(gòu)的對(duì)象集合(具體結(jié)構(gòu)在開(kāi)發(fā)過(guò)程中無(wú)法確定)
B.希望對(duì)這些對(duì)象或者其中的某些對(duì)象執(zhí)行某種操作
(2)組合模式的缺點(diǎn)
因?yàn)榻M合對(duì)象的任何操作都會(huì)對(duì)所有的子對(duì)象調(diào)用同樣的操作,所以當(dāng)組合的結(jié)構(gòu)很大時(shí)會(huì)有性能問(wèn)題。還有就是使用組合模式封裝HTML時(shí)要選擇合適的標(biāo)簽,比如table就不能用于組合模式,葉子節(jié)點(diǎn)不明顯