组合模式设计

一、设计模式

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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 发送提示消息## 考虑这样一个实际的业务功能:发送提示消息。基本上所有带业务流程处理的系统...
    七寸知架构阅读 4,912评论 5 63
  • 一、使用组合模式的场景 把部分和整体的关系用树形结构来表示,从而使客户端可以用统一的方式处理部分对象和整体对象 二...
    yjaal阅读 385评论 0 0
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,875评论 1 15
  • 一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者...
    RamboLI阅读 727评论 0 1
  • 【今日种下的与此目标相关的种子】 1. 早晨持续给龙称法王日行一善捐款,让慈悲心逐日成长,让每天的这滴水融入大海以...
    李鲜花阅读 359评论 0 4