一、设计模式

javascript里面给我们提供了很多种设计模式:

工厂、桥、组合、门面、适配器、装饰者、享元、代理、观察者、命令、责任链

在前面我们实现了工厂模式和桥模式

工厂模式 :

核心:为了生产对象,实现解耦。

桥接模式 :

(桥接模式是一种既能把两个对象连接在一起,又能避免二者间的强耦合的方法。通过“桥”把彼此联系起来,同时又允许他们各自独立变化)

主要作用:主要作用表现为将抽象与其实现隔离开来,以便二者独立化。

组合模式 :

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

优点 :

1 你可以用同样的方法处理对象的集合与其中的特定子对象。

2 它可以用来把一批子对象组织成树形结构,并且使整棵树都可以被遍历。

场景 :

1 存在一批组织成某种层次体系的对象

2 希望对这批对象或其中的一部分对象实施一个操作。

特点 :

1 组合模式中只有两种类型对象:组合对象、叶子对象

2 这两种类型都实现同一批接口

3 一般我们会在组合对象中调用其方法并隐式调用"下级对象"的方法(这里我们一般采用递归的形式去做)

后面的模式后面具体用到在细说

看实例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>组合设计模式</title>

        <!--
        @theme: javascript高级 组合模式设计
        @autor:EthanCoco
        @date:2015-11-22
        @email:lijianlin0204@163.com
        -->

        <script type=text/javascript charset=utf-8>
        //创建一个命名空间
        var LJL = {};

        /**
        *建一个接口
        *接口需要两个参数
        *参数1: 接口的名字(string)
        *参数2: 方法名称(数组(string))
        */
        LJL.Interface = function(name,methods){
            if(arguments.length !== 2){//判断接口参数个数是否正确
                throw new Error("参数个数不正确!");
            }
            this.name = name;
            this.methods = [];//定义一个内置的空数组接受方法名称里的元素
            for(var i = 0;i < methods.length;i++){
                if(typeof methods[i] !== 'string'){
                    throw new Error("方法名称错误,必须是字符串类型!");
                }
                //把传入参数的元素全部放到内置的methods中去
                this.methods.push(methods[i]);
            }

        };

        /**
        *接口静态方法
        *参数:object
        *检验接口里的方法是否实现
        *如果通过不做任何操作,如果不通过,抛出error
        *目的:检测方法是否全部实现
        *object 要求参数必须有两个以上
        *一个是对象实例
        *其它是要检验的接口对象
        */
        LJL.Interface.checkMethodsIsPass = function(object){
            if(arguments.length < 2){//判断参数,如果参数小于2,抛出异常
                throw new Error("要求参数必须有两个以上@param1=实例对象,其它参数是接口对象!");
            }
            //获得接口实例对象
            for(var i = 1;i<arguments.length;i++){//i=1是因为第二个参数是需要检测的接口
                var  instanceInterface = arguments[i];
                //判断参数是否是接口对象
                if(instanceInterface.constructor !== LJL.Interface){
                    throw new Error("参数是不是接口对象!");
                }
                //如果是,检测接口对象里的方法是否实现
                for(var j = 0;j<instanceInterface.methods.length;j++){
                    //用历史变量接受一个方法的名称,名称是字符串,如果不是就抛出error
                    var methodName = instanceInterface.methods[j];
                    //object[key]表示的就是方法
                    //方法是一个函数,需要判断
                    if(!object[methodName] || typeof object[methodName] !== 'function'){
                        throw new Error("这个方法 '" + methodName + "' 找不到!");
                    }
                }

            }

        };

        /**
         * 继承方法
         * @param {Object} sub
         * @param {Object} sup
         */
        /*
        LJL.extend=function(sub ,sup){
             // 目的: 实现只继承父类的原型对象
             var F = new Function();    // 1 创建一个空函数    目的:空函数进行中转
             F.prototype = sup.prototype; // 2 实现空函数的原型对象和超类的原型对象转换
             sub.prototype = new F();     // 3 原型继承
             sub.prototype.constructor = sub ; // 4还原子类的构造器
             //保存一下父类的原型对象: 一方面方便解耦  另一方面方便获得父类的原型对象
             sub.superClass = sup.prototype; //自定义一个子类的静态属性 接受父类的原型对象
             //判断父类的原型对象的构造器 (加保险)
             if(sup.prototype.constructor == Object.prototype.constructor){
                sup.prototype.constructor = sup ; //手动欢迎父类原型对象的构造器
             }
        };
        */
        /**
         * 扩展Array的原型对象 添加变量数组的每一个元素,并让每一个元素都执行fn函数 (可变量多维数组)
         * @param {Object} fn
         */
        Array.prototype.each = function(fn){
            try{
                //1 目的: 遍历数组的每一项 //计数器 记录当前遍历的元素位置
                this.i || (this.i=0);  //var i = 0 ;
                //2 严谨的判断什么时候去走each核心方法
                // 当数组的长度大于0的时候 && 传递的参数必须为函数
                if(this.length >0 && fn.constructor == Function){
                    // 循环遍历数组的每一项
                    while(this.i < this.length){    //while循环的范围
                        //获取数组的每一项
                        var e = this[this.i];
                        //如果当前元素获取到了 并且当前元素是一个数组
                        if(e && e.constructor == Array){
                            // 直接做递归操作
                            e.each(fn);
                        } else {
                            //如果不是数组 (那就是一个单个元素)
                            // 这的目的就是为了把数组的当前元素传递给fn函数 并让函数执行
                            //fn.apply(e,[e]);
                            fn.call(e,e);
                        }
                        this.i++ ;
                    }
                    this.i = null ; // 释放内存 垃圾回收机制回收变量
                }

            } catch(ex){
                // do something
            }
            return this ;
        }

        /********************************************************/
        ////////////////////////////////////////////////
        //以上都是为设计模式做必要的准备
        ////////////////////////////////////////////////

        //开始组合设计模式

        /**
          *  组合模式应用的场景和特点:
          *  场景:
          *  1 存在一批组织成某种层次体系的对象
          *  2 希望对这批对象或其中的一部分对象实施一个操作
          *
          *  应用特点:
          *  1 组合模式中只有两种类型对象:组合对象、叶子对象
          *  2 这两种类型都实现同一批接口
          *  3 一般我们会在组合对象中调用其方法并隐式调用"下级对象"的方法(这里我们一般采用递归的形式去做)
          *
          */
        /*
         * 场景模拟:
         *  -> 公司
         *       -> 北京分公司
         *                    -> 财务部门
         *                                -> 张1
         *                                -> 张2
         *                                -> 张3
         *                    -> 销售部门
         *                                -> 张4
         *                                -> 张5
         *                                -> 张6
                 -> 长沙分公司
         *                    -> 财务部门
         *                                -> 张7
         *                                -> 张8
         *                                -> 张9
         *                    -> 销售部门
         *                                -> 张10
         *                                -> 张11
         *                                -> 张12
         *
         *    实际的任务具体是落实到人上去实施的 也就是说只有人才具有具体的方法实现
         */        

        //创建组合对象的接口实例
        var CompositeInterface = new LJL.Interface('CompositeInterface' , ['addChild','getChild']);
        //创建叶子对象的接口实例
        var LeafInterface = new LJL.Interface('LeafInterface' , ['hardworking','sleeping']);
        /********************************************************/

        /***********************组合对象*********************************/
        //首先 : 组合模式中只有两种类型对象:组合对象、叶子对象
        //创建组合对象
        var Composite = function(name){
            this.name = name;
            this.type = 'Composite';        //说明对象的类型(组合对象)
            this.children = [] ;             //承装孩子的数组
            //然后 :这两种类型都实现同一批接口
            //创建对象的最后要验证接口
            LJL.Interface.checkMethodsIsPass(this,CompositeInterface,LeafInterface);
        };

        //在原型对象上实现接口方法
        Composite.prototype = {
            constructor:Composite , //还原构造器
            //实现CompositeInterface接口的addChildh和getChild方法
            addChild:function(child){
            //添加子节点到children上
                this.children.push(child);
                return this;//返回控制权,实现链式操作
            },
            getChild:function(name){
                //定义一个数组接受叶子对象类型
                var elements = [] ;
                //判断对象是否是叶子对象类型,如果是添加到数组中去
                //如果不是,则运用递归继续调用
                var pushLeaf = function(item){
                    if(item.type === 'Composite'){
                            item.children.each(arguments.callee);
                    } else if(item.type === 'Leaf'){
                            elements.push(item);
                    }
                };

                // 根据name 让指定name下的所有的类型为Leaf的对象去执行操作
                if(name && this.name !== name){
                    this.children.each(function(item){
                        // 如果传递的name是2级节点名称
                        if(item.name === name && item.type === 'Composite'){
                            item.children.each(pushLeaf);
                        }
                        // 如果传递的name是3级节、4级、5级...N级
                        if(item.name !== name && item.type === 'Composite'){
                            item.children.each(arguments.callee);
                        }
                        // 如果传递的name是叶子节点的时候
                        if(item.name === name && item.type === 'Leaf'){
                            elements.push(item);
                        }
                    });
                }else{  // 不传递name 让整个公司所有类型为Leaf的对象去执行操作
                    this.children.each(pushLeaf);
                }
                return elements ;
            },
            //实现LeafInterface接口的hardworking和sleeping方法
            hardworking:function(name){
                //得到所有的Leaf类型的对象数组
                var leafObjects = this.getChild(name);
                for(var i = 0 ; i < leafObjects.length; i ++){
                    leafObjects[i].hardworking();
                }
            },
            sleeping:function(name){
                //得到所有的Leaf类型的对象数组
                var leafObjects = this.getChild(name);
                for(var i = 0 ; i < leafObjects.length; i ++){
                    leafObjects[i].sleeping();
                }
            }
        };
        /***********************组合对象*********************************/

        /***********************叶子对象*********************************/
        //同样在叶子原型对象上实现接口方法
        var Leaf = function(name){
            this.name = name;
            this.type = 'Leaf';        //说明对象的类型(叶子对象)
            //创建对象的最后要验证接口
            LJL.Interface.checkMethodsIsPass(this,CompositeInterface,LeafInterface);
        };

        Leaf.prototype = {
            constructor:Leaf ,//还原构造器
            //实现CompositeInterface接口的addChildh和getChild方法
            addChild:function(child){
            //让其不能使用这个方法
                throw new Error('this method is disabled....');
            },
            getChild:function(name){
                if(this.name = name){
                    return this ;
                }
                return null ;
            },
            //实现LeafInterface接口的hardworking和sleeping方法
            hardworking:function(){
                document.write(this.name + '...努力工作!');
            },
            sleeping:function(){
                document.write(this.name + '...努力睡觉!');
            }
        };
         /***********************叶子对象*********************************/

         /***********************测试单元*********************************/
         //测试数据
         //创建人的叶子对象
         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');

         //创建公司部门
         var dept1 = new Composite('北京开发部门');
         //把p1,p2,p3三个人指定到dept1中去
         dept1.addChild(p1).addChild(p2).addChild(p3);
         var dept2 = new Composite('北京销售部门');
         dept2.addChild(p4).addChild(p5).addChild(p6);
         var dept3 = new Composite('长沙开发部门');
         dept3.addChild(p7).addChild(p8).addChild(p9);
         var dept4 = new Composite('长沙销售部门');
         dept4.addChild(p10).addChild(p11).addChild(p12);    

         //创建组织分公司
         var org1 = new Composite('北京分公司');
         //把dept1和dept2指定到org1中去
         org1.addChild(dept1).addChild(dept2);
         var org2 = new Composite('长沙分公司');
         org2.addChild(dept3).addChild(dept4);    

         //创建总部
         var org = new Composite('尚学堂总部');
         //把分公司挂到总部
         org.addChild(org1).addChild(org2);

         // 让整个公司下所有的员工都去努力工作
         org.hardworking();     //尚学堂总部
         document.write('<Br>----------------------------------<Br>');
         // name为总公司的直接子节点的时候
         org.hardworking('长沙分公司');
         document.write('<Br>----------------------------------<Br>');
         // name为总公司的间接子节点的时候(类型不为leaf)(3级4级...N级)
         org.hardworking('长沙开发部门');
         document.write('<Br>----------------------------------<Br>');
         // name为leaf对象的时候
         org.hardworking('张5');
         document.write('<Br>----------------------------------<Br>');
        /***********************测试单元*********************************/
        </script>
    </head>
    <body>
    </body>
</html>

JavaScript高级---组合模式设计的更多相关文章

  1. JavaScript高级---门面模式设计

    门面模式 两个作用: 1.简化类的接口 2.消除类与使用它的客户代码之间的耦合 门面模式常常是开发人员最亲密的朋友.它几乎是所有javascript库的核心原则 门面模式的目的是为了让开发人员用更简单 ...

  2. JavaScript高级---桥模式设计

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  3. javascript设计模式-组合模式

    组合模式所要解决的问题: 可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象.可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象. 客户端代码必须区别对象简单对象和 ...

  4. [设计模式] javascript 之 组合模式

    组合模式说明 组合模式用于简单化,一致化对单组件和复合组件的使用:其实它就是一棵树: 这棵树有且只有一个根,访问入口,如果它不是一棵空树,那么由一个或几个树枝节点以及子叶节点组成,每个树枝节点还包含自 ...

  5. 读书笔记之 - javascript 设计模式 - 组合模式

    组合模式是一种专为创建Web上的动态用户界面而量身定制的模式,使用这种模式,可以用一条命令在对各对象上激发复杂的或递归的行为. 在组合对象的层次体系中有俩种类型对象:叶对象和组合对象.这是一个递归定义 ...

  6. javascript设计模式——组合模式

    前面的话 在程序设计中,有一些和“事物是由相似的子事物构成”类似的思想.组合模式就是用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成的.本文将详细介绍组合模式 宏命令 宏 ...

  7. JavaScript设计模式-组合模式(表单应用实现)

    书读百遍其义自见 <JavaScript设计模式>一书组合模式在表单中应用,我问你答答案. 注:小编自己根据书中的栗子码的答案,如有错误,请留言斧正. 另:如有转载请注明出处,谢谢啦 &l ...

  8. Java 设计模式 —— 组合模式

    在现实生活中,存在很多"部分-整体"的关系,例如,大学中的部门与学院.总公司中的部门与分公司.学习用品中的书与书包.生活用品中的衣服与衣柜.以及厨房中的锅碗瓢盆等.在软件开发中也是 ...

  9. JavaScript高级---装饰者模式设计

    一.设计模式 javascript里面给我们提供了很多种设计模式: 工厂.桥.组合.门面.适配器.装饰者.享元.代理.观察者.命令.责任链 在前面我们实现了工厂模式和桥模式 工厂模式 : 核心:为了生 ...

随机推荐

  1. 【python】python文件和目录操作方法大全(含实例)

    转自:http://www.jb51.net/article/48001.htm 一.python中对文件.文件夹操作时经常用到的os模块和shutil模块常用方法.1.得到当前工作目录,即当前Pyt ...

  2. 网站添加到IIS和附件进程调试(新手使用篇)

    一.网站添加到IIS 做网站开发,很有必要把项目添加到IIS中,这对浏览和后期的调试很有帮助.怎么把网站添加到IIS上? 1). 打开IIS,然后操作步骤如下图: 选择Default Web Site ...

  3. 一些常用css技巧的为什么(一)我所理解的margin

    要用到的基本术语和概念: 正常流:HTML文档的文本布局,在非西方语言中流的方向可能不同.大多数元素都在正常流中,浮动或定位可以让元素脱离正常流. 块级元素:像p,div之类的元素在正常流中会在其框之 ...

  4. [javascript|基本概念]学习笔记

    1/语法 a.区分大小写 b.标识符(首字符必须是字母/"_"/"$",其他可为字母/"_"/"$"/数字,不能用关键字 ...

  5. 济南学习 Day 5 T1 pm

    欧拉函数(phi)题目描述: 已知(N),求phi(N). 输入说明: 正整数N. 输出说明: 输出phi(N). 样例输入: 8 样例输出: 4 数据范围: 对于20%的数据,N<=10^5 ...

  6. 字节的高低位知识,Ascii,GB2312,UNICODE等编码的关系与来历

    很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称为"字节". 再后来,他们又做了一些可以处理 ...

  7. visifire3.6.8 去水印方法

    visifire 很NB的一套开源图表 不多介绍 详询google 3.0以下版本可以直接继承Chart类 override 加水印的函数就可以, 3.0以上版本需要自己编译源代码 这个水印函数藏得有 ...

  8. yum最常用的命令

    yum是一个用于管理rpm包的后台程序,用python写成,可以非常方便的解决rpm的依赖关系.在建立好yum服务器后,yum客户端可以通过 http.ftp方式获得软件包,并使用方便的命令直接管理. ...

  9. C string 函数大全

    PS:本文包含了大部分strings函数的说明,并附带举例说明.本来想自己整理一下的,发现已经有前辈整理过了,就转了过来.修改了原文一些源码的问题,主要是用char *字义字符串的问题,导致程序运行时 ...

  10. 1014. Waiting in Line (30)

    Suppose a bank has N windows open for service. There is a yellow line in front of the windows which ...