《JavaScript设计模式与开发实践》模式篇(7)—— 组合模式

组合模式将对象组合成树形结构,以表示“部分-总体”的层次结构。 除了用来表示树形结构以外,组合模式的另外一个好处是经过对象的多态性表现,使得用户对单个对象和组合对象的使用具备一致性程序员

以命令模式中的宏命令代码为例,宏命令对象包含了一组具体的子命令对象,不论是宏命令对象,仍是子命令对象,都有一个execute方法负责执行命令。宏命令中包含了一组子命令,它们组成了一个树形结构,这里是一棵结构很是简单的树 编程

宏命令树结构

在组合模式中,请求在树中传递的过程老是遵循一种逻辑。请求从树最顶端的对象往下传递,若是当前处理请求的对象是叶对象(普通子命令),叶对象自身会对请求做出相应的处理;若是当前处理请求的对象是组合对象(宏命令), 组合对象则会遍历它属下的子节点,将请求继续传递给这些子节点。设计模式

组合模式请求传递方式

  • 组合模式下的宏命令 目前的万能遥控器,包含了关门、开电脑、登陆 QQ 这 3 个命令。如今咱们须要一个“超级万能遥控器”,能够控制家里全部的电器,这个遥控器拥有如下功能
    • 打开空调
    • 打开电视和音响
    • 关门、开电脑、登陆 QQ
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 );
复制代码

小结

组合模式可让咱们使用树形方式创 建对象的结构。咱们能够把相同的操做应用在组合对象和单个对象上。在大多数状况下,咱们都 能够忽略掉组合对象和单个对象之间的差异,从而用一致的方式来处理它们。 然而,组合模式并非完美的,它可能会产生一个这样的系统:系统中的每一个对象看起来都 与其余对象差很少。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外,若是经过组合模式建立了太多的对象,那么这些对象可能会让系统负担不起。学习

系列文章:

《JavaScript设计模式与开发实践》最全知识点汇总大全ui