构成格局

如今的话

  在先后设计中,有1些和“事物是由一般的子事物构成”类似的思虑。组合格局就是用小的子对象来营造越来越大的靶子,而这么些小的子对象自笔者或许是由越来越小的“孙对象”构成的。本文将详细介绍组合形式

 

宏命令

  宏命令对象涵盖了一组具体的子命令对象,不管是宏命令对象,照旧子命令对象,都有一个execute方法负责执行命令。今后记忆一下一声令下方式中关于万能遥控器的宏命令代码:

var closeDoorCommand = {
    execute: function(){
        console.log( '关门' );
    }
};
var openPcCommand = {
    execute: function(){
        console.log( '开电脑' );
    }
};
var openQQCommand = {
    execute: function(){
        console.log( '登录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 macroCommand = MacroCommand();
macroCommand.add( closeDoorCommand );
macroCommand.add( openPcCommand );
macroCommand.add( openQQCommand );
macroCommand.execute();

  宏命令中包蕴了壹组子命令,它们构成了3个树形结构,那里是一棵结构极度不难的树

皇冠直营现金网开户 1

皇冠直营现金网开户, 

  其中,marcoCommand被称呼组合对象,closeDoorCommand、openPcCommand、openQQCommand都以子对象。在macroCommand的execute方法里,并不实施真正的操作,而是遍历它所包蕴的子对象,把真正的execute请求委托给那一个子对象。macroCommand表现得像三个限令,但它实质上只是一组真正命令的“代理”。并非真的的代办,固然结构上相似,但macroCommand只担负传递请求给子对象,它的指标不在于控制对子对象的造访

  组合情势将指标组合成树形结构,以表示“部分——全部”的层次结构。除了用于表示树形结构之外,组合情势的另3个利益是由此对象的多态性表现,使得用户对单个对象和组合对象的运用具有一致性

  一、表示树形结构。组合方式的优点:提供了一种遍历树形结构的方案,通过调用组合对象的execute方法,程序会递归调用组合对象上面包车型大巴子对象的execute方法。组合情势能够十一分便于地讲述对象部分——全部层次结构

  二、利用对象多态性统壹对待组合对象和单个对象。利用目的的多态性表现,能够使客户端忽略组合对象和单个对象的不如。在组成情势中,客户将统壹地使用组合结构中的全数目的,而不供给关注它到底是构成对象依旧单个对象

  那在实质上支付中会给客户带来非常的大的便利性,往万能遥控器里面添加三个指令时,并不拥戴那么些命令是宏命令或然普通子命令。只必要分明它是2个限令,并且那些命令拥有可举办的execute方法,那么那么些命令就足以被加上

  当宏命令和普通子命令接收到执行execute方法的乞请时,宏命令和普通子命令都会做它们分别认为正确的工作。那些差异是逃匿在客户背后的,在客户看来,这种透明性能够11分自由地壮大程序

  在组成格局中,请求在树中传递的历程接连坚守一种逻辑。以宏命令为例,请求从树最上边的目的往下传递,要是当前处理请求的靶子是子对象(普通子命令),子对象自作者会对请求作出相应的处理;假若当前处理请求的靶子是结合对象(宏命令),组合对象则会遍历它属下的子节点,将呼吁继续传递给那个子节点

  一句话来说,假设子节点是子对象,子对象自作者会处理那几个请求,而如若实节点照旧组成对象,请求会持续往下传递。子对象上边不会再有别的子节点,2个子目标就是树的那条枝叶的无尽,组合对象上面或者还会有子节点

  请求从上到下沿着树进行传递,直到树的底限。作为客户,只必要关爱树最顶层的组成对象,客户只须要请求那几个组成对象,请求便会沿着树往下传递,依次到达全数的子对象

  近期的无所无法遥控器,包罗了关门、开电脑、登录QQ那二个指令。未来亟需三个“超级万能遥控器”,能够操纵家里全体的电器,这几个遥控器拥有以下功效:打开中央空调、打开电视机和音响、关门、开电脑、登录QQ

  首先在节点中放置一个按钮button来表示这么些一流万能遥控器,一级万能遥控器上设置了1个宏命令,当执行那些宏命令时,会相继遍历执行它所富含的子命令,代码如下:

<button id="button">按我</button>
<script>
    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 );
</script>

  当按下遥控器的按钮时,全体命令都将被每种执行

  基本目的足以被组合成更复杂的咬合对象,组合对象又能够被重组,那样持续递归下去,这棵树的协会能够扶助任意多的复杂度。在树最后被组织达成之后,让整颗树最后运转起来的步骤相当简单,只必要调用最上层对象的execute方法。每当对最上层的目的进行1回呼吁时,实际上是在对总体树进行深度优先的搜寻,而创制组合对象的程序员并不关怀这个内在的底细,往那棵树里面添加一些新的节点目的是卓殊简单的业务

  组合情势最大的长处在于能够一如既往地对待组合对象和骨干指标。客户不要求知道当前拍卖的是宏命令也许普通命令,只要它是一个下令,并且有execute方法,那个命令就能够被添加到树中

  在javascript那种动态类型语言中,对象的多态性是与生俱来的,也远非编写翻译器去反省变量的种类,javascript中贯彻组合格局的难处在于要有限帮衬组合对象和子对象目的拥有一致的主意,那平常须要用鸭子类型的盘算对它们实行接口检查

  在javascript中落到实处组合情势,看起来缺少一些严俊性,代码算不上安全,但能更赶快和随机地付出,那既是javascript的瑕疵,也是它的亮点

  组合形式的透明性使得发起呼吁的客户不用去顾忌树中结合对象和基本指标的界别,但它们在本质上是有分别的

  组合对象足以具有子节点,基本对象下边就从未有过子节点,所以只怕会发出壹些误操作,比如试图往基本对象中添加子节点。化解方案平日是给大旨目的也扩大add方法,并且在调用那么些办法时,抛出二个13分来马上提醒客户

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 openTvCommand = {
    execute: function(){
        console.log( '打开电视' );
    },
    add: function(){
        throw new Error( '基本对象不能添加子节点' );
    }
};

var macroCommand = MacroCommand();
macroCommand.add( openTvCommand );
openTvCommand.add( macroCommand );

 

举目四望文件夹

  文件夹和文件之间的涉嫌,卓殊适合用整合方式来描述。文件夹里既能够包括文件,又能够蕴含其余文件夹,最终可能组合成壹棵树,组合方式在文件夹的行使中有以下两层好处

  一、组合情势让Ctrl+V、Ctrl+C成为了五个合并的操作。例如,在移动硬盘里找到了有些电子书,想把它们复制到F盘中的学习资料文件夹。在复制那些电子书的时候,并不必要考虑那批文件的项目,不管它们是单身的电子书还是被放在了文本夹中

  2、用杀毒软件扫描该公文夹时,往往不会关心里面有个别许文件和子文件夹,组合格局使得只须求操作最外层的文件夹实行围观

  今后来编排代码,首先分别定义好文件夹Folder和文书File那八个类。见如下代码:

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++ ]; ){
        file.scan();
    }
};
var File = function( name ){
    this.name = name;
};
File.prototype.add = function(){
    throw new Error( '文件下面不能再添加文件' );
};
File.prototype.scan = function(){
    console.log( '开始扫描文件: ' + this.name );
};

  接下去成立1些文件夹和文件对象,并且让它们组合成一棵树,那棵树正是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 );

  通过那一个例子,再一次看到客户是怎么着天公地道组合对象和主导对象的。在添加一堆文件的操作进度中,客户不要分辨它们毕竟是文件或许文件夹。新扩张的公文和文书夹能够很简单地拉长到原来的树结构中,和树里已部分对象一起工作。改变了树的构造,扩充了新的数量,却不要修改任何一句原有的代码,那是适合开放——封闭原则的

  运用了组合情势之后,扫描整个文件夹的操作也是毫不费力的,只供给操作树的最上端对象:

folder.scan();

 

注意事项

  在行使组合情势的时候,还有以下多少个值得注意的地方

  一、组合方式不是老爹和儿子关系组合格局的树型结构不难令人误以为组合对象和主导指标是老爹和儿子关系。组合格局是1种HAS-A(聚合)的关系,而不是IS-A。组合对象涵盖一组基本指标,但Leaf并不是Composite的子类。组合对象把请求委托给它所富含的保有骨干目的,它们能够合营的严重性是具有一致的接口

  二、对子对象操作的1致性。组合形式除了必要结合对象和子对象具备相同的接口之外,还有一个要求条件,正是对1组子对象的操作必须具有1致性。比如集团要给整个职工发放元春的过节费一千块,这几个现象能够使用组合情势,但如果公司给明天过生日的职工发送壹封出生之日祝福的邮件,组合方式在此间就不曾用武之地了,除非先把明天过破壳日的职工挑选出去。唯有用同样的艺术对待列表中的每一个子对象的时候,才合乎选取组合方式

  三、双向映射关系。发放过节费的布告步骤是从公司到各类部门,再到各样小组,最终到各种职工的信箱里。那作者是三个组合情势的好例子,但要思量的一种状态是,或者某个职员和工人属于八个公司架构。比如某位架构师既隶属于开发组,又隶属于架构组,对象时期的关系并不是严俊意义上的层次结构,在那种状态下,是不吻合利用组合形式的,该架构师很大概会吸收两份过节费。那种复合情形下,必须给父节点和子节点建立双向映射关系,三个简单的形式是给小组和职员和工人对象都扩充集合来保存对方的引用。然则那种相互间的引用分外复杂,而且对象之间发生了过多的耦合性,修改可能去除二个对象都变得紧Baba,此时得以引进中介者情势来管理那个指标

  四、用职分链形式升高组合方式质量。在组成形式中,假设树的结构相比复杂,节点数量过多,在遍历树的进程中,质量方面大概表现得不够理想。有时候的确能够依靠1些技术,在实操中防止遍历整棵树,有一种现成的方案是信赖职分链情势。职责链形式1般供给手动去设置链条,但在结合方式中,父对象和子对象时期其实形成了自然的任务链。让请求顺着链条从父对象往子对象传递,或许是扭曲从子对象往父对象传递,直到碰着能够拍卖该请求的指标截止,那也是天职链形式的经典运用境况之一

 

引用父对象

  组合对象保存了它下边包车型大巴子节点的引用,那是整合形式的天性,此时树结构是从上至下的。但偶尔供给在子节点上保证对父节点的引用,比如在整合格局中利用任务链时,有十分的大可能率须要让请求从子节点往父节点上冒泡传递。还有当删除某个文件时,实际上是从那个文件所在的上层文件夹中去除该公文的

  以后来改写扫描文件夹的代码,使得在扫描整个文件夹从前,能够先移除某一个有血有肉的文件

  首先改写Folder类和File类,在那三个类的构造函数中,扩充this.parent属性,并且在调用add方法的时候,正确安装文件或然文件夹的父节点:

var Folder = function( name ){
    this.name = name;
    this.parent = null; //增加this.parent 属性
    this.files = [];
};

Folder.prototype.add = function( file ){
    file.parent = this; //设置父对象
    this.files.push( file );
};

Folder.prototype.scan = function(){
    console.log( '开始扫描文件夹: ' + this.name );
    for ( var i = 0, file, files = this.files; file = files[ i++ ]; ){
        file.scan();
    }
};

  接下去增添Folder.prototype.remove方法,表示移除该公文夹。在Folder.prototype.remove方法里,首先会判定this.parent,尽管this.parent为null,那么那么些文件夹要么是树的根节点,要么是还尚未拉长到树的游离节点,那时候未有节点供给从树中移除,暂时让remove方法间接return,表示不做其它操作。借使this.parent不为null,则表达该公文夹有父节点存在,此时遍历父节点中保存的子节点列表,删除想要删除的子节点

Folder.prototype.remove = function(){
    if ( !this.parent ){ //根节点或者树外的游离节点
        return;
    }
    for ( var files = this.parent.files, l = files.length - 1; l >=0; l-- ){
        var file = files[ l ];
        if ( file === this ){
            files.splice( l, 1 );
        }
    }
};

  File类的兑现基本一致:

var File = function( name ){
    this.name = name;
    this.parent = null;
};

File.prototype.add = function(){
    throw new Error( '不能添加在文件下面' );
};

File.prototype.scan = function(){
    console.log( '开始扫描文件: ' + this.name );
};

File.prototype.remove = function(){
    if ( !this.parent ){ //根节点或者树外的游离节点
        return;
    }

    for ( var files = this.parent.files, l = files.length - 1; l >=0; l-- ){
        var file = files[ l ];
        if ( file === this ){
            files.splice( l, 1 );
        }
    }
};

  上边测试一下移除文件效用:

var folder = new Folder( '学习资料' );
var folder1 = new Folder( 'JavaScript' );
var file1 = new Folder ( '深入浅出Node.js' );

folder1.add( new File( 'JavaScript 设计模式与开发实践' ) );
folder.add( folder1 );
folder.add( file1 );
folder1.remove(); //移除文件夹
folder.scan();

  组合格局1旦选取妥当,可以大大简化客户的代码。一般的话,组合格局适用于以下那三种情形

  1、表示对象的局地——全体层次结构。组合方式能够一本万利地布局1棵树来表示对象的一部分——全体布局。越发是在开发时期不鲜明那棵树到底存在多少层次的时候。在树的布局最后成就之后,只必要通过请求树的最顶层对象,便能对整棵树做联合的操作。在重组方式中追加和删除树的节点卓殊便宜,并且符合开放——封闭原则

  二、客户愿意统一对待树中的全体指标。组合方式使客户能够忽略组合对象和子对象的区分,客户在面对这棵树的时候,不用关注当前正值处理的靶子是组成对象如故子对象,也就绝不写一群if、else语句来分别处理它们。组合对象和子对象会分别做本人不利的事体,这是构成格局最要紧的能力

  然则,组合方式并不是两全的,它也许会生出2个这么的系统:系统中的各种对象看起来都与其余对象差不离。它们的界别只有在运营的时候会才会显现出来,那会使代码难以领会。其余,假使由此整合形式创造了太多的对象,那么这么些目的或然会让系统负责不起

 

相关文章