JavaScript设计模式之----组合模式

介绍

组合模式是一种专门为创建Web上的动态用户界面而量身制定的模式。使用这种模式可以用一条命令在多个对象上激发复杂的或递归的行为。这可以简化粘合性代码,使其更容易维护,而那些复杂行为则被委托给各个对象。

组合模式的好处:

  • 1 你可以用同样的方法处理对象的集合与其中的特定子对象。
  • 2 它可以用来把一批子对象组织成树形结构,并且使整棵树都可以被遍历。

正文

组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

常见的场景有asp.net里的控件机制(即control里可以包含子control,可以递归操作、添加、删除子control),类似的还有DOM的机制,一个DOM节点可以包含子节点,不管是父节点还是子节点都有添加、删除、遍历子节点的通用功能。所以说组合模式的关键是要有一个抽象类,它既可以表示子元素,又可以表示父元素。

场景

    组合模式场景:   -->公司
                            -->财务部门
                                    -->张一
                                    -->张二
                                    -->张三

                            -->销售部门
                                    -->张四
                                    -->张五
                                    -->张六
    实际任务是具体落实到人上去实施的,也就是说
    只有人才具有具体的方法实现

<!DOCTYPE html>
<html>
<head>
    <script src="./commonUtil.js" type="text/javascript" charset="utf-8" ></script>

    <title>组合模式</title>
</head>
<body>
    <script>
    /*
        组合模式场景:   -->公司
                                -->财务部门
                                        -->张一
                                        -->张二
                                        -->张三

                                -->销售部门
                                        -->张四
                                        -->张五
                                        -->张六
        实际任务是具体落实到人上去实施的,也就是说
        只有人才具有具体的方法实现
    */

    //定义一个公司
    var Orgnazation = function(name){
        this.name = name ;
        this.departments= [];

    };
    Orgnazation.prototype = {
        constructor: Orgnazation,
        addDepts : function(childOrgnazation){
            this.departments.push(childOrgnazation);
            //链式调用
            return this;
        }, 
        getDepts: function(){
            return this.departments;
        }

    };

    //定义一个部门
    var Department = function(name){
        this.name = name ;
        this.persons = [];
    };
    Department.prototype = {
        constructor:Department,
        addPersons : function (childPerson){
            this.persons.push(childPerson);
            return this;
        },
        getPersons: function(){
            return this.persons;
        }
    };


    //定义一个人
    var Person = function(name){
        this.name = name ;
    };
    Person.prototype ={
        constructor:Person,
        hardworking:function(name){
            document.write(this.name + "....好好上班!");
        },
        sleeping: function(name){
            document.write(this.name + "....好好休息!");

        }
    };


    /*需求:1、实例化上述情景,2、叫张三好好工作*/
    var org = new Orgnazation("华为");
    var dept1 = new Department("财务部门");
    var dept2 = new Department("销售部门");
    var p1 = new Person("张1");
    var p2 = new Person("张2");
    var p3 = new Person("张3");
    var p4 = new Person("张4");
    var p5 = new Person("张5");
    var p6 = new Person("张6");


    dept1.addPersons(p1).addPersons(p2).addPersons(p3);
    dept2.addPersons(p4).addPersons(p5).addPersons(p6);
    org.addDepts(dept1).addDepts(dept2);

    for(var i =0 , depts = org.getDepts(); i <depts.length ; i ++ ){
        for (var j = 0, persons = depts[i].getPersons(); j < persons.length; j++){
            if (persons[j].name =="张3") {
                persons[j].hardworking();
            };
        }

    }
    /*
        上述方法完全是面向对象的方法,实现的上述需求,如果要给变功能(比如说,财务部全体放假,整个公司好好休息!增加一个新部门,在好好工作。。。。。),怎要动怎么for循环,这样不是我们想要的,如果我们引入组合模式,是不是能解决效果呢?

        直接 org.hardworking("华为")
    */

    </script>

</body>
</html>

组合模式的使用场景:

    1 存在一批组织成某种层次体系的对象
    2 希望对这批对象或其中的一部分对象实施一个操作。
    组合模式的特点:
    1 组合模式中只有两种类型对象:组合对象、叶子对象
    2 这两种类型都实现同一批接口
    3 一般我们会在组合对象中调用其方法并隐式调用"下级对象"的方法(这里我们一般采用递归的形式去做)
*/

/*
    组合模式场景:   -->公司
                          -->北京分公司
                                -->财务部门
                                        -->张一
                                        -->张二
                                        -->张三

                                -->销售部门
                                        -->张四
                                        -->张五
                                        -->张六
                          -->南京分公司
                                -->开发部门
                                        -->张7
                                        -->张8
                                        -->张9

                                -->法务部门
                                        -->张10
                                        -->张11
                                        -->张12


    实际任务是具体落实到人上去实施的,也就是说
    只有人才具有具体的方法实现

<!DOCTYPE html>
<html>
<head>
    <script src="./commonUtil.js" type="text/javascript" charset="utf-8" ></script>

    <title>组合模式2</title>
</head>
<body>
    <script>

    /*
            组合模式的使用场景:
        1 存在一批组织成某种层次体系的对象
        2 希望对这批对象或其中的一部分对象实施一个操作。
        组合模式的特点:
        1 组合模式中只有两种类型对象:组合对象、叶子对象
        2 这两种类型都实现同一批接口
        3 一般我们会在组合对象中调用其方法并隐式调用"下级对象"的方法(这里我们一般采用递归的形式去做)
    */

    /*
        组合模式场景:   -->公司
                              -->北京分公司
                                    -->财务部门
                                            -->张一
                                            -->张二
                                            -->张三

                                    -->销售部门
                                            -->张四
                                            -->张五
                                            -->张六
                              -->南京分公司
                                    -->开发部门
                                            -->张7
                                            -->张8
                                            -->张9

                                    -->法务部门
                                            -->张10
                                            -->张11
                                            -->张12


        实际任务是具体落实到人上去实施的,也就是说
        只有人才具有具体的方法实现
    */

    //定义接口
    var CompositeInterface = new XGP.Interface("CompositeInterface", ["addChild","getChild"]);
    var LeafInterface = new XGP.Interface("LeafInterface", ["hardworking","sleeping"]);


    // 首先建立组合对象
    var Composite = function(name){
        this.name = name ;
        this.type = "Composite"; //说明对象的类型为(组合对象)
        this.children = [];         //承装孩子的数组
    };

    //建立叶子对象
    var Leaf = function(name){
        this.name = name ;
        this.type = "Leaf";
    };

    //实现同一批接口
    Composite.prototype = {
        constructor: Composite, 
        addChild:function(child){
            this.children.push(child);
            return this;
        }, 

        //难点!!!!
        getChild:function(name){
            //接收叶子对象
            var elments =[];

            var pushLeaf = function(item){
                    if(item.type ==="Composite"){
                        item.children.each(arguments.callee);//看是否是Composite还是Leaf;
                    }else{
                        elments.push(item);//直接添加到叶子对象集合
                    }
                });


            if (name && this.name !== name) {//根据name ,让name下的Compsite的所有Leaf去执行操作
                this.children.each(function(item){
                    //如果是2级节点名称---北京分公司,南京分公司
                    if (item.name === name && item.type =="Composite") {
                        item.children.each(pushLeaf);

                    };
                    //如果是3级,4级。。。。N级
                    if (item.name !== name && item.type =="Composite") {
                        item.children.each(arguments.callee);
                    };

                    //如果传递的就是叶子节点
                    if (item.name == name && item.type =="Leaf") {
                        elments.push(item);
                    };
                });

            }else{//不传名字,则整个公司的所有类型的leaf 类型执行操作
                //Array.prototype.each()方法----添加到commomUtil.js
                this.children.each(pushLeaf); //直接抽取到pushLeaf函数中
            };
        }, 
        hardworking: function(name){
            //对于Composite类型的对象,要先获得其下面所有的Leaf对象
            var leafObjects = this.getChild(name);
            for (var i = 0; i < leafObjects.length; i++) {
                leafObjects[i].hardworking();
            };
        },
        sleeping: function(){
            //对于Composite类型的对象,要先获得其下面所有的Leaf对象
            var leafObjects = this.getChild(name);
            for (var i = 0; i < leafObjects.length; i++) {
                leafObjects[i].sleeping();
            };
        }

    };

    Leaf.prototype = {
        constructor: Leaf, 
        addChild:function(child){
            throw new Error("this method is disable...");
        }, 
        getChild:function(name){
            if(this.name = name ){
                return this;
            };
            return null;
        }, 
        hardworking: function(){
            document.write(this.name + "....好好上班!");

        },
        sleeping: function(){
            document.write(this.name + "....好好休息!");
        }

    };



    //测试数据
    var org = new CompositeInterface("华为");
    var suborg1 = new CompositeInterface("北京分公司");
    var suborg2 = new CompositeInterface("南京分公司");
    var dept1 = new CompositeInterface("财务部门");
    var dept2 = new CompositeInterface("销售部门");
    var dept3 = new CompositeInterface("开发部门");
    var dept4 = new CompositeInterface("法务部门");
    var p1 = new Leaf("张1");
    var p2 = new Leaf("张2");
    var p3 = new Leaf("张3");
    var p4 = new Leaf("张4");
    var p5 = new Leaf("张5");
    var p6 = new Leaf("张6");
    var p7 = new Leaf("张7");
    var p8 = new Leaf("张8");
    var p9 = new Leaf("张9");
    var p10 = new Leaf("张10");
    var p11 = new Leaf("张11");
    var p12 = new Leaf("张12");


    dept1.addChild(p1).addChild(p2).addChild(p3);
    dept2.addChild(p4).addChild(p5).addChild(p6);
    dept3.addChild(p7).addChild(p8).addChild(p9);
    dept2.addChild(p10).addChild(p11).addChild(p12);
    suborg1.addChild(dept1).addChild(dept2);
    suborg2.addChild(dept3).addChild(dept4);
org.addChild(suborg1).addChild(suborg2);
    //让公司整个员工都去努力工作
    org.hardworking();
    document.write("<Br>--------------------<Br>");
    org.hardworking("北京分公司");
    document.write("<Br>--------------------<Br>");
    org.hardworking("南京分公司");
    document.write("<Br>--------------------<Br>");
    org.hardworking("法务部门");
    document.write("<Br>--------------------<Br>");
    org.hardworking("张5");
    </script>

</body>
</html>

总结

组合模式的使用场景非常明确:

你想表示对象的部分-整体层次结构时;

你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象(方法)

另外该模式经常和装饰者一起使用,它们通常有一个公共的父类(也就是原型),因此装饰必须支持具有add、remove、getChild操作的 component接口。

参考: https://github.com/tcorral/Design-Patterns-in-Javascript/blob/master/Composite/index.html

http://www.cnblogs.com/TomXu/archive/2012/04/12/2435530.html

版权声明:本文为小平果原创文章,转载请注明:http://blog.csdn.net/i10630226