組合模式將對象組合成樹形結構,以表示「部分-總體」的層次結構。 除了用來表示樹形結構以外,組合模式的另外一個好處是經過對象的多態性表現,使得用戶對單個對象和組合對象的使用具備一致性程序員
以命令模式中的宏命令代碼爲例,宏命令對象包含了一組具體的子命令對象,不論是宏命令對象,仍是子命令對象,都有一個execute方法負責執行命令。宏命令中包含了一組子命令,它們組成了一個樹形結構,這裏是一棵結構很是簡單的樹 編程
在組合模式中,請求在樹中傳遞的過程老是遵循一種邏輯。請求從樹最頂端的對象往下傳遞,若是當前處理請求的對象是葉對象(普通子命令),葉對象自身會對請求做出相應的處理;若是當前處理請求的對象是組合對象(宏命令), 組合對象則會遍歷它屬下的子節點,將請求繼續傳遞給這些子節點。設計模式
var MacroCommand = function(){
return {
commandsList: [],
add: function( command ){
this.commandsList.push( command );
},
execute: function(){
for ( var i = 0, command; command = this.commandsList[ i++ ]; ){
command.execute();
}
}
}
};
var openAcCommand = {
execute: function(){
console.log( '打開空調' );
}
};
/*家裏的電視和音響是鏈接在一塊兒的,因此能夠用一個宏命令來組合打開電視和打開音響的命令*/
var openTvCommand = {
execute: function(){
console.log( '打開電視' );
}
};
var openSoundCommand = {
execute: function(){
console.log( '打開音響' );
}
};
var macroCommand1 = MacroCommand();
macroCommand1.add(openTvCommand);
macroCommand1.add(openSoundCommand);
/*關門、打開電腦和打登陸 QQ 的命令*/
var closeDoorCommand = {
execute: function(){
console.log( '關門' );
}
};
var openPcCommand = {
execute: function(){
console.log( '開電腦' );
}
};
var openQQCommand = {
execute: function(){
console.log( '登陸 QQ' );
}
};
var macroCommand2 = MacroCommand();
macroCommand2.add( closeDoorCommand );
macroCommand2.add( openPcCommand );
macroCommand2.add( openQQCommand );
/*如今把全部的命令組合成一個「超級命令」*/
var macroCommand = MacroCommand();
macroCommand.add( openAcCommand );
macroCommand.add( macroCommand1 );
macroCommand.add( macroCommand2 );
/*最後給遙控器綁定「超級命令」*/
var setCommand = (function( command ){
document.getElementById( 'button' ).onclick = function(){
command.execute();
}
})( macroCommand );
複製代碼
從這個例子中能夠看到,基本對象能夠被組合成更復雜的組合對象,組合對象又能夠被組合, 這樣不斷遞歸下去,這棵樹的結構能夠支持任意多的複雜度。在樹最終被構造完成以後,讓整顆 樹最終運轉起來的步驟很是簡單,只須要調用最上層對象的 execute 方法。每當對最上層的對象 進行一次請求時,其實是在對整個樹進行深度優先的搜索,而建立組合對象的程序員並不關心這些內在的細節,往這棵樹裏面添加一些新的節點對象是很是容易的事情。bash
文件夾和文件之間的關係,很是適合用組合模式來描述。文件夾裏既能夠包含文件,又能夠 包含其餘文件夾,最終可能組合成一棵樹 當使用用殺毒軟件掃描該文件夾時,每每不會關內心面有多少文件和子文件夾,組合模式使得咱們只須要操做最外層的文件夾進行掃描。post
/* Folder */
var Folder = function( name ){
this.name = name;
this.files = [];
};
Folder.prototype.add= function( file ){
this.files.push(file );
};
Folder.prototype.scan = function(){
console.log( '開始掃描文件夾: ' + this.name );
for ( var i = 0, file, files = this.files; file = files[ i++ ]; ){
files file.scan();
}
};
/*File*/
var File = function( name ){
this.name = name;
};
File.prototype.add = function(){
throw new Error( '文件下面不能再添加文件' );
};
File.prototype.scan = function(){
console.log( '開始掃描文件: ' + this.name );
};
/*建立一些文件夾和文件對象, 而且讓它們組合成一棵樹,這棵樹就是咱們 F 盤裏的 現有文件目錄結構*/
var folder = new Folder( '學習資料' );
var folder1 = new Folder( 'JavaScript' );
var folder2 = new Folder ( 'jQuery' );
var file1 = new File( 'JavaScript 設計模式與開發實踐' );
var file2 = new File( '精通 jQuery' );
var file3 = new File('重構與模式' );
folder1.add( file1 );
folder2.add( file2 );
folder.add( folder1 );
folder.add( folder2 );
folder.add( file3 );
/*如今的需求是把移動硬盤裏的文件和文件夾都複製到這棵樹中,假設咱們已經獲得了這些文件對象*/
var folder3 = new Folder( 'Nodejs' );
var file4 = new File( '深刻淺出 Node.js' );
folder3.add( file4 );
var file5 = new File( 'JavaScript 語言精髓與編程實踐' );
/*接下來就是把這些文件都添加到原有的樹中*/
folder.add( folder3 );
folder.add( file5 );
複製代碼
組合模式可讓咱們使用樹形方式創 建對象的結構。咱們能夠把相同的操做應用在組合對象和單個對象上。在大多數狀況下,咱們都 能夠忽略掉組合對象和單個對象之間的差異,從而用一致的方式來處理它們。 然而,組合模式並非完美的,它可能會產生一個這樣的系統:系統中的每一個對象看起來都 與其餘對象差很少。它們的區別只有在運行的時候會纔會顯現出來,這會使代碼難以理解。此外,若是經過組合模式建立了太多的對象,那麼這些對象可能會讓系統負擔不起。學習