组合模式将对象组合成树形结构,以表示“部分-总体”的层次结构。 除了用来表示树形结构以外,组合模式的另外一个好处是经过对象的多态性表现,使得用户对单个对象和组合对象的使用具备一致性程序员
以命令模式中的宏命令代码为例,宏命令对象包含了一组具体的子命令对象,不论是宏命令对象,仍是子命令对象,都有一个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 );
复制代码
组合模式可让咱们使用树形方式创 建对象的结构。咱们能够把相同的操做应用在组合对象和单个对象上。在大多数状况下,咱们都 能够忽略掉组合对象和单个对象之间的差异,从而用一致的方式来处理它们。 然而,组合模式并非完美的,它可能会产生一个这样的系统:系统中的每一个对象看起来都 与其余对象差很少。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外,若是经过组合模式建立了太多的对象,那么这些对象可能会让系统负担不起。学习