Javascript设计模式

转载自:https://www.cnblogs.com/chenxygx/p/5754101.html

Javascript设计模式

Javascript设计模式记录,这个方面确实是没写过,工作中也没有用到js设计模式的地方。

prototype与面向对象取舍

使用prototype原型继承和使用面向对象,都可以实现闭包的效果。那么这两个的选择点,就是方法会不会产生多个实例

例如,我们需要做一个闭包数组,并给他提供一个添加方法。

1!(function () { 2//原型继承写法 3varValidator =function(){ 4this.cache = []; 5    }; 6Validator.prototype.add =function(item){ 7this.cache.push(item); 8    }; 9varvalidator =newValidator(),validatorr =new Validator();10validator.add("test1"); console.log(validator.cache);11validatorr.add("test2"); console.log(validatorr.cache);12//面向对象写法13varValidator2 = {14        cache : [],15add :function(item){16this.cache.push(item);17        }18    };19Validator2.add("test3"); console.log(Validator2.cache);20Validator2.add("test4"); console.log(Validator2.cache);21})()

这两种写法都可以实现闭包,但是面向对象的写法,只能存在一个。我们无法对他进行初始化,而原型继承写法,我们则可以对他进行初始化操作。

所以当,我们认为这个方法,在整个程序中,是唯一的存在。我们可以使用面向对象的写法,如果可以存在多个,则使用prototype这种写法。

调用父类构造函数

继承关系的两个对象,在实例的过程中,可以通过修改指向,来调整调用构造函数。

!(function () {

    varA =function (light) {

        this.light1 = light;

    };

    varB =function (light) {

        this.light = light;

        A.apply(this,arguments);//你需要手动调用A的构造方法    };

    //给B赋值的同时,给A赋值B.prototype =new A();

    varC =newB(123);

    console.log(C.light);

    console.log(C.light1);

})()

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。例如:线程池,全局缓存,登录浮窗。

首先我们需要把单例的逻辑代码单独提取,然后使用惰性单例的方式,也就是返回方法。只有在点击的时候,才会进行执行。

javascript的单例,跟类不一样。无需创建多余的构造函数这些,直接创建全局变量即可。

1!(function () { 2//管理单例的逻辑代码,如果没有数据则创建,有数据则返回 3vargetSingle =function(fn){//参数为创建对象的方法 4var result; 5returnfunction(){//判断是Null或赋值 6returnresult || (result = fn.apply(this,arguments)); 7      }; 8  }; 9//创建登录窗口方法10varcreateLoginLayer =function(){11vardiv = document.createElement('div');12div.innerHTML = '我是登录浮窗';13div.style.display = 'none';14        document.body.appendChild(div);15return div;16    };17//单例方法18varcreateSingleLoginLayer = getSingle(createLoginLayer);1920//使用惰性单例,进行创建21document.getElementById('loginBtn').onclick =function(){22varloginLayer = createSingleLoginLayer();23loginLayer.style.display = 'block';24    };25})()

策略模式

定义一系列的算法,把它们一个一个封装起来。将算法的使用与算法的实现分离开来。

javascript的策略模式很简单,把算法直接定义成函数即可。

1!(function () { 2//定义算法方法 3varstrategies = { 4"S":function(salary){ 5returnsalary * 4; 6        }, 7"A":function(salary){ 8returnsalary * 3; 9        },10"B":function(salary){11returnsalary * 2;12        }13    };14//执行算法15varcalculateBouns =function(level,salary){16return strategies[level](salary);17    };18console.log(calculateBouns('S',2000));19})()

undefined终止循环

(写具体代码之前,先记录一个知识点)。当循环表达式为undefined时,循环会终止。

!(function(){

    varcale = [1,2,3];

    for(vari= 0,validate;validate=cale[i];)

    {

        cale.shift();

        console.log(validate);

    }

})() //1,2,3

登录验证表单

下面写一个使用策略模式,制作的验证表单登录效果。

传统的表单登录效果,会在提交后进行一系列的判断验证。这样提交方法很庞大,而且缺少弹性,复用性也很差。

我们可以使用策略模式,来避免这些问题。

<form action="post" id="registerForm"><input type="text" name="userName"/><input type="text" name="password"/><input type="text" name="phoneNumber"/><button>提交</button></form>

!(function () {

    //定义验证规则,使用策略模式,直接通过 strategies[isNonEmpty]()可以访问varstrategies = {

        isNonEmpty: function(value, errorMsg) {//不为空if(value === "") {

                return errorMsg;

            }

        },

        minLength: function(value, length, errorMsg) {//最小长度if(value.length < length) {

                return errorMsg;

            }

        },

        isMobile: function(value, errorMsg) {//手机号码格式if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

                return errorMsg;

            }

        }

    };

    //创建验证逻辑,使用闭包定义全局数组,存放验证方法varValidator =function () {

        this.cache = [];

    };

    //添加验证逻辑方法,参数:元素,验证名称,错误信息Validator.prototype.add =function (dom, rules) {

        varself =this;

        for(vari = 0, rule; rule = rules[i++];) {

            (function (rule) {

                varary = rule.strategy.split(":");//限制最小数值,进行分割。如没有:号,则直接返回varerrorMsg = rule.errorMsg;

                self.cache.push(function() {//将操作方法封装到全局数组中varstrategy = ary.shift();//获取验证方法名称,并删除ary.unshift(dom.value);//往开头添加待验证元素ary.push(errorMsg);//添加验证失败错误信息returnstrategies[strategy].apply(dom, ary);//传递数组给方法,因为不涉及this,dom也可传递null                });

            })(rule)

        }

    };

    //添加启动方法Validator.prototype.start =function () {

        //将数组中的方法,分别执行。数组undefined,则跳出循环for(vari = 0, validatorFunc; validatorFunc =this.cache[i++];) {

            varmsg = validatorFunc();

            //又失败就进行跳出if (msg) {

                return msg;

            }

        }

    };

    //处理校验varvalidatorFunc =function () {

        varvalidator =newValidator();//创建验证逻辑\//添加验证条件        validator.add(registerForm.userName, [

            {

                strategy: 'isNonEmpty',

                errorMsg: '用户不能为空'            }, {

                strategy: 'minLength:2',

                errorMsg: '用户不能少于2位'            }]);

        validator.add(registerForm.password, [

            {

                strategy: 'minLength:6',

                errorMsg: '密码长度不能少于6位'            }]);

        validator.add(registerForm.phoneNumber, [

            {

                strategy: 'isMobile',

                errorMsg: '手机号码格式不正确'            }]);

        varerrorMsg = validator.start();

        return errorMsg;

    };

    varregisterForm = document.getElementById("registerForm");

    registerForm.onsubmit =function () {

        varerrorMsg = validatorFunc();

        if(errorMsg) {//判断是否有这个参数            alert(errorMsg);

            returnfalse;

        }

    };

})()

果然,有逻辑的js,非常好玩。比CSS好玩多了。感觉有很多委托、多态的思想。

策略模式的优点与缺点

有效的避免许多重复的复制粘贴作业。

开闭原则的完美支持,算法完全独立易于切换、理解、拓展。

算法复用性强

使用组合和委托让Validator类拥有执行算法的能力,也是继承的一种轻便替代方式

缺点

会增加许多策略类或策略对象。

违反迪米特法则,会将strategy暴露给客户所有实现。

代理模式

为一个对象提供一个代用品或占位符,以便控制对它的访问。当客户不方便直接访问一个对象的时候,需要提供一个替身对象来控制对这个对象的访问。

代理模式分为:虚拟代理和保护代理

虚拟代理:把一些开销很大的对象,延迟到真正需要它的时候才去创建。

保护代理:用于控制不同权限的对象对目标对象的访问。

图片预加载

使用虚拟代理可以完成图片预加载功能,先用一张loading图片占位,然后用异步方式加载图片,等图片加载完毕后填充到img节点里。

因为javascript事件,均为异步事件。所以当执行proxyImage时,会先设置loading.gif,等图片加载完毕后,会执行myImage操作。

varmyImage = (function(){

      varimgNode = document.createElement('img');

      document.body.appendChild(imgNode);

      return {

          setSrc:function(src){

              imgNode.src = src;

          }

      };

  })();//预加载方法varproxyImage = (function(){

        varimg =new Image();

        img.onload =function(){

            myImage.setSrc(this.src);

        }

        return {

            setSrc:function(src){

                myImage.setSrc("loading.gif");

                img.src = src;

            }

        };

    })();

    proxyImage.setSrc('实际图片.jpg');//预加载myImage.setSrc('实际图片'.jpg);//普通加载

注意:加载方法和预加载方法,必须使用立即执行函数,不然setSrc方法调用不到。

如上预加载功能,之所以使用代理模式,主要是为了避免违反,单一职责设计原则。

如不使用代理模式,会执行加载图片和预加载操作。当我们不需要预加载功能的时候,无法进行快速隔离。

虚拟代理中的惰性加载

将虚拟代理运用到惰性加载中,可以让真实的代码延迟到真正实用的时候才进行添加。

varminiConsole = (function () {

        varcache = [];

        varhandler =function(ev) {//监听按键事件if(ev.keyCode === 113) {

                varscript = document.createElement('script');

                script.onload =function () {

                    for(vari = 0, fn; fn = cache[i++];) {

                        fn();

                    }

                };

                script.src = 'minConsole.js';

                document.getElementsByTagName('head')[0].appendChild(script);

                document.body.removeEventListener('keydown', handler);//只加载一次            }

        };

        document.body.addEventListener('keydown', handler,false);

        return {

            log: function () {

                varargs = arguments;

                cache.push(function () {

                    return miniConsole.log.apply(miniConsole, args);

                });

            }

        };

    })();

    miniConsole.log(11);

    miniConsole = {

        log: function () {

            console.log(Array.prototype.join.call(arguments));

        }

    };

缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,可以使用之前的计算结果。

例如使用缓存代理计算乘积

varmult =function () {

        vara = 1;

        for(vari = 0, l = arguments.length; i < l; i++) {

            a = a * arguments[i];

        }

        return a;

    }

    //不使用缓存mult(2,3);varproxyMult = (function () {

        varcache = {};

        returnfunction () {

            varargs = Array.prototype.join.call(arguments, ',');//把参数放在一个字符串里if(argsin cache) {

                return cache[args];

            }

            returncache[args] = mult.apply(this,arguments);

        };

    })();

    //使用缓存proxyMult(2,3)

代理工厂 

传入高阶函数可以为各种计算方法创建缓存代理。将计算方法传入专门用于创建缓存代理的工厂中,这样就可以创建缓存代理了。

这是使用策略模式,进行创建的一种写法。可以直接定义方法即可。

varstrate = {

        mult: function () {

            vara = 1;

            for(vari = 0, l = arguments.length; i < l; i++) {

                a = a * arguments[i];

            }

            return a;

        },

        plus: function () {

            vara = 0;

            for(vari = 0, l = arguments.length; i < l; i++) {

                a = a + arguments[i];

            }

            return a;

        }

    };

    varcreateProxyFactory =function (fn) {

        varcache = {};

        returnfunction () {

            varargs = Array.prototype.join.call(arguments, ',');

            if(argsin cache) {

                return cache[args];

            }

            returncache[args] = fn.apply(this, arguments);

        };

    };

    varproxyMult = createProxyFactory(strate["mult"]);

    console.log(proxyMult(1,2,3,4));

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

例如事件绑定,就是一个标准的观察者模式。

document.body.addEventListener('click',function(){

      console.log(2);

    },false);

    document.body.click();

下面进行一个实际的例子,售楼处可以接受买房登记,登记后的用户如果有房源,则会逐一告知。 并且可以进行取消登记操作。

为了彻底结束耦合性,可以使用全局变量制作监听事件。

varObserverEvent = (function () {

        varclientList = [], listen, trigger, remove;

        listen =function (key, fn) {

            if(!clientList[key]) {

                clientList[key] = [];

            }

            clientList[key].push(fn);

        };

        trigger =function () {

            varkey = Array.prototype.shift.call(arguments), fns = clientList[key];

            if(!fns || fns.length === 0) {

                returnfalse;

            }

            for(vari = 0, fn; fn = fns[i++];) {

                fn.apply(this, arguments);

            }

        };

        remove =function (key, fn) {

            varfns = clientList[key];

            if(!fns) {

                returnfalse;

            }

            if(!fn) {

                fns && (fns.length = 0);

            } else {

                for(varl = fns.length - 1; l >= 0; l--) {

                    var_fn = fns[l];

                    if(_fn === fn) {

                        fns.splice(l, 1);

                    }

                }

            }

        };

        return {

            listen:listen,

            trigger:trigger,

            remove:remove

        }

    })();

    ObserverEvent.listen('squareMeter88', fn1 =function (price) {

        console.log('价格=' + price);

    });

    ObserverEvent.listen('squareMeter100',function (price) {

        console.log('价格=' + price);

    });

    ObserverEvent.trigger('squareMeter88', 200000);

    ObserverEvent.trigger('squareMeter100', 300000);

    ObserverEvent.remove('squareMeter88', fn1);

    ObserverEvent.trigger('squareMeter88', 200000);

当然这种售楼处只是一个例子,在现实中,登录页面登录后,会需要刷新各个模块的信息(头像、nav)这类。我们也可以使用观察者模式进行刷新操作。

我们直接改用调用方法即可,而且是完全的解耦合

varheader = (function () {

        ObserverEvent.listen('loginSucc',function (data) {

            header.setAvatar(data.avatar);

        });

        return {

            setAvatar: function (data) {

                console.log(data + "设置header成功");

            }

        }

    })();

    varnav = (function () {

        ObserverEvent.listen('loginSucc',function (data) {

            nav.setAvatar(data.avatar)

        });

        return {

            setAvatar: function (data) {

                console.log(data + '设置nav成功');

            }

        }

    })();

    vardata = {};

    data.avatar = "参数";

    ObserverEvent.trigger('loginSucc', data);

观察者模式的优点很明显:时间上的解耦,对象之间的解耦。

命令模式

命令模式指的是一个执行某些特定事情的指令。常见的应用场景是:有时候需要向对象发送请求,但不知道接受者是谁,也不知道请求的操作是什么。

例如:订餐,客人需要给厨师发送请求,至于那个厨师做,做的步骤。客人不知道。这就是命令模式。

命令模式的一个简单例子

//定义命令模式执行varsetCommand =function (button, func) {

        button.onclick =function () {

            func.execute();

        };

    };

    varMenuBar = {

        refresh: function () {

            console.log("刷新页面");

        }

    };

    varRefreshMenuBarCommand =function (receiver) {

        return {

            execute: function () {

                receiver.refresh();

            }

        }

    };

    varrefreshMenuBarCommand = RefreshMenuBarCommand("MenuBar");

    setCommand(button1,refreshMenuBarCommand);

命令模式的用处很大,也可以做撤销命令,回放这种功能。比如,我们把用户按键命令做一个封装,制作一个播放功能。

以下代码可以执行并记录按键,当点击按钮时,会执行按键对应动作。

//定义按键动作varRyu = {

        W: function () {

            console.log("用户按下W");

        },

        S: function () {

            console.log("用户按下S");

        }

    };

    //创建命令varmakeCommand =function (receiver, state) {

        returnfunction () {

            if(receiver[state])

                receiver[state]();

        }

    };

    //可执行按键Jsonvarcommands = {

        "119": "W",

        "115": "S"    };

    //保存按键记录varcommandStack = [];

    document.onkeypress =function (ev) {

        varkeyCode = ev.keyCode, command = makeCommand(Ryu, commands[keyCode]);

        if (command) {

            command();

            commandStack.push(command);

        }

    };

    //注册按键监听document.getElementById("replay").addEventListener('click',function () {

        var commad;

        while(command = commandStack.shift()) {

            command();

        }

    }, false);

宏命令

宏命令可以一次执行一组命令。我们定义了各种指令,定义了如何执行指令。就可以做成一组命令的这种模式了。

针对不同的步骤,也可以只用此方法。例如: 

var macroCommand2 = new MacroCommand();

macroCommand2.add(macroCommand);

macroCommand2.execute();

可以指定不同命令。

!(function () {

    varcloseDoorCommand = {

        execute: function () {

            console.log("关门");

        }

    };

    varopenPcCommand = {

        execute: function () {

            console.log("开电脑");

        }

    };

    varopenQQCommand = {

        execute: function () {

            console.log("登录QQ");

        }

    };

    varMacroCommand =function(){

      return {

          commandsList:[],

          add:function(command){

              this.commandsList.push(command);

          },

          execute:function(){

              for(vari= 0,command;command=this.commandsList[i++];){

                  command.execute();

              }

          }

      };

    };

    varmacroCommand =new MacroCommand();

    macroCommand.add(closeDoorCommand);

    macroCommand.add(openPcCommand);

    macroCommand.execute();

})()

模板方法模式

模板方法是一种只需要继承就可以实现的非常简单的模式。封装了子类的算法框架,包含一些公共方法一级封装子类中的所有方法执行顺序。

咖啡与茶。咖啡的步骤:1.烧水,2.冲泡,3.倒进杯子,4.放牛奶。茶叶的步骤:1.烧水,2.浸泡,3.倒进杯子,4.加柠檬

我们分离不同点:2,4。然后使用抽象父类定义,并实现对应方法。其中的init方法,就是模板方法,因为他封装了子类算法框架就,作为一个算法的模板。

!(function () {

  varBeverage =function(){};

    Beverage.prototype.boilWater =function(){//烧水console.log("把水煮沸");

    };

    Beverage.prototype.brew =function(){};//第二步,方法Beverage.prototype.pourInCup =function(){};

    Beverage.prototype.addCondiments=function(){};

    Beverage.prototype.init =function(){

        this.boilWater();

        this.brew();

        this.pourInCup();

        this.addCondiments();

    };

    //创建咖啡子类varCoffee =function(){};

    Coffee.prototype =new Beverage();

    Coffee.prototype.brew =function(){

        console.log("用沸水冲泡咖啡");

    };

    Coffee.prototype.pourInCup =function(){

        console.log("把咖啡倒进杯子");

    };

    Coffee.prototype.addCondiments =function(){

        console.log("加糖加牛奶");

    };

    varCoffee =new Coffee();

    Coffee.init();

})()

针对模板方法,有一些个性的子类,不打算接受模板约束。那么可以使用钩子方法来创建。

我们修改模板方法让他适应钩子方法。

Beverage.prototype.customerWantsCondiments =function () {

        returntrue;

    };

    Beverage.prototype.init =function () {

        this.boilWater();

        this.brew();

        this.pourInCup();

        if(this.customerWantsCondiments()) {

            this.addCondiments();

        }

    };

    //创建咖啡子类varCoffee =function () {

    };

    Coffee.prototype =new Beverage();

    Coffee.prototype.customerWantsCondiments =function(){

        returnwindow.confirm("请问需要调料吗");

    }

享元模式 

享元模式的核心是运用共享技术来有效支持大量细粒度的对象。

例如,现在有50件男装和50件女装,分别需要模特穿上并且拍照。如果我们不使用享元模式,那么就需要new 100个模特。

使用享元模式,只需要new 2个模特,然后让他们穿上不同的衣服即可。

 享元模式

享元模式的使用取决于:一个程序中使用了大量相似对象。造成很大的内存开销。大多数状态是外部状态。可以用较少的功效对象取代大量对象。

对象池 

对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接new,而是转从对象池里获取。如果没有空闲对象则创建,完成职责后再次进入池子。

我们做一个公用的对象池,来维护新建dom对象。

!(function () {

    varobjectPoolFactory =function (createObjFn) {

        varobjectPool = [];

        return {

            create: function () {

                varobj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();

                return obj;

            },

            recover: function (obj) {

                objectPool.push(obj);

            }

        };

    };

    variframeFactory = objectPoolFactory(function () {

        variframe = document.createElement("iframe");

        document.body.appendChild(iframe);

        iframe.onload =function () {

            iframe.onload =null;

            iframeFactory.recover(iframe);

        }

        return iframe

    });

    variframe1 = iframeFactory.create();

    iframe1.src = "http://www.baidu.com";

    setTimeout(function(){

        variframe2 = iframeFactory.create();

        iframe2.src = "http://www.baidu.com";

    },2000);

})()

职责链模式

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将对象形成一条链,并沿着这条链传递请求。

使用orderType和pay来控制流向。分别进行不同对象的流转。

!(function () {

    varorder500 =function (orderType, pay, stock) {

        if(orderType === 1 && pay ===true) {

            console.log("500元定金");

        } else {

            order200(orderType, pay, stock);

        }

    };

    varorder200 =function (orderType, pay, stock) {

        if(orderType === 2 && pay ===true) {

            console.log("200元定金");

        } else {

            orderNormal(orderType, pay, stock);

        }

    };

    varorderNormal =function (orderType, pay, stock) {

        if(stock > 0) {

            console.log("普通购买");

        } else {

            console.log("手机库存不足");

        }

    };

    order500(1,true,500);

})()

但是这种责任链体系,耦合度比较高,例如500对象与200对象,耦合度很高。

!(function () {

    varorder500 =function (orderType, pay, stock) {

        if(orderType === 1 && pay ===true) {

            console.log("500元定金");

        } else {

            return"nextSuccessor";

        }

    };

    varorder200 =function (orderType, pay, stock) {

        if(orderType === 2 && pay ===true) {

            console.log("200元定金");

        } else {

            return"nextSuccessor";

        }

    };

    varorderNormal =function (orderType, pay, stock) {

        if(stock > 0) {

            console.log("普通购买");

        } else {

            console.log("手机库存不足");

        }

    };

    varChain =function(fn){

        this.fn = fn;

        this.success =null;

    };

    Chain.prototype.setNextSuccessor =function(successor){

        returnthis.success = successor;

    };

    Chain.prototype.passRequest =function(){

        varret =this.fn.apply(this,arguments);

        if(ret === "nextSuccessor"){

            returnthis.success &&this.success.passRequest.apply(this.success,arguments);

        }

    };

    varchainOrder500 =new Chain(order500);

    varchainOrder200 =new Chain(order200);

    chainOrder500.setNextSuccessor(chainOrder200);

    chainOrder500.passRequest(2,true,200);

})()

中介者模式

中介者模式的作用是解除对象与对象之间的紧耦合关系。增加中介者后,所有的相关对象都通过中介者对象来通信。

泡泡堂例子

1!(function () { 2varplayerDirector = (function () { 3varplayers = {}, operations = {}; 4operations.addPlayer =function (player) { 5varteamColor = player.teamColor; 6players[teamColor] = players[teamColor] || []; 7            players[teamColor].push(player); 8        }; 9operations.removePlayer =function (player) {10varteamColor = player.teamColor, teamPlayers = players[teamColor] || [];11for(vari = teamPlayers.length - 1; i >= 0; i++) {12if(teamPlayers[i] === player) {13teamPlayers.splice(i, 1);14                }15            }16        };17operations.changeTeam =function (player, newTeamColor) {18            operations.removePlayer(player);19player.teamColor = newTeamColor;20            operations.addPlayer(player);21        };22operations.playerDead =function (player) {23varteamColor = player.teamColor, teamPlays = players[teamColor];24varall_dead =true;25for(vari = 0, player; player = teamPlays[i++];) {26if(player.state !== "dead") {27all_dead =false;28break;29                }30            }31if(all_dead ===true) {32for(vari = 0, player; player = teamPlays[i++];) {33                    player.lose();34                }35for(varcolorin players) {36if(color != teamColor) {37varteamPlayers = players[color];38for(vari = 0, player; player = teamPlayers[i++];) {39                            player.win();40                        }41                    }42                }43            }44        };45varReceiveMessage =function () {46varmessage = Array.prototype.shift.call(arguments);47operations[message].apply(this, arguments);48        };49return {50            ReceiveMessage:ReceiveMessage51        };52    })();53function Player(name, teamColor) {54this.name = name;55this.teamColor = teamColor;56this.state = "alive";57    }58Player.prototype.win =function () {59console.log(this.name + " win ");60    };61Player.prototype.lose =function () {62console.log(this.name + " lose ");63    }64Player.prototype.die =function () {65this.state = "dead";66playerDirector.ReceiveMessage("playerDead",this);67    };68Player.prototype.remove =function () {69playerDirector.ReceiveMessage("removePlayer",this);70    };71Player.prototype.changeTeam =function (color) {72playerDirector.ReceiveMessage("changeTeam",this, color);73    };74varPlayerFacotry =function (name, teamColor) {75varnewPlayer =new Player(name, teamColor);76playerDirector.ReceiveMessage("addPlayer", newPlayer);77return newPlayer;78    };7980//测试81varplayer1 = PlayerFacotry("皮蛋","red"),82player2 = PlayerFacotry("小乖","red");83varplayer3 = PlayerFacotry("黑妞","blue"),84player4 = PlayerFacotry("葱头","blue");85    player1.die();player2.die();86})()

中介者模式是迎合迪米特法则的一种实现。指一个对象应该尽可能的少了解另外的对象。如果对象之间的耦合性太高,一个对象改变后,会影响其他对象。

装饰者模式

装饰者模式可以动态的给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。

!(function () {

    varplance = {

        fire:function(){

            console.log("发射普通子弹");

        }

    };

    varmissileDecorator =function(){

        console.log("发射导弹");

    };

    varfire1 = plance.fire;

    plance.fire =function(){

        fire1();

        missileDecorator();

    };

    plance.fire();

})()

或者使用装饰函数(AOP)Function的after或者before

!(function () {

    varplance =function () { };

    plance.prototype.fire =function () {

        console.log("发射普通子弹");

    };

    varmissileDecorator =function () {

        console.log("发射导弹");

    };

    Function.prototype.after =function (afterfn) {

        var_self =this;

        returnfunction () {

            varret = _self.apply(this, arguments);

            afterfn.apply(this, arguments);

            return ret;

        };

    };

    varpl =new plance();

    pl.fire = pl.fire.after(missileDecorator);

    pl.fire();

})()

装饰函数是一个很实用的功能,例如我们制作插件式的表单验证,就可以使用装饰函数。

!(function () {

    varregisterForm = document.getElementById("registerForm");

    Function.prototype.before =function(beforeFn){

        var_self =this;

        returnfunction(){

            if(beforeFn.apply(this,arguments) ===false){

                return;

            }

            return_self.apply(this,arguments);

        };

    }

    varvalidata =function () {

        if(registerForm.userName.value === ""){

            alert("用户名不能为空");

            returnfalse;

        }

    };

    varformSubmit =function(){

        console.log("成功");

    }

    formSubmit = formSubmit.before(validata);

    registerForm.onsubmit =function(){

        formSubmit();

        returnfalse;

    };

})()

代理模式OR装饰器模式

代理模式和装饰器模式相似的地方很多,都是有单独的一个对象提供间接访问。他们最大的不同就是设计的意图和目的。

代理模式的目的是:当直接访问本体不方便或者不符合需求时,为这个本体提供一个替代者。

装饰器模式的目的是:为对象动态加入一些行为。

例如图片预加载,代理提供预加载功能是调用原来的方法也就是跟本体做的事情一样,而装饰器模式则是添加新的职责和行为。

状态模式

状态模式关键是区分事物内部的状态,事物内部状态改变会带来事物行为的改变。

第一个例子:电灯开关,同样是按下开关,电灯亮或者不亮,表达的行为是不一样的。buttonWasPressed 方法是变化的点。

!(function () {

    varLightEvent = {

        on:function(){

            console.log("关灯");

            this.state = "off";

        },

        off:function(){

            console.log("开灯");

            this.state = "on";

        }

    };

    varLight =function () {

        this.state = "off";

        this.button =null;

    };

    Light.prototype.init =function(){

        varbutton = document.createElement("button"),self=this;

        button.innerHTML = "开关";

        this.button = document.body.appendChild(button);

        this.button.onclick =function(){

            self.buttonWasPressed();

        };

    };

    Light.prototype.buttonWasPressed =function(){

        LightEvent[this.state].apply(this,arguments);

    };

    varlight =new Light();

    light.init();

})()

上一个例子使用策略模式来完成的,我们来说一下策略模式与状态模式的区别

策略模式中各个策略类是平等又平行的,他们之间没有任何联系。状态类的行为早已被封装好了,改变行为发生在状态模式内部。

那么我是使用状态模式,来完成上面电灯的例子。

!(function () {

    vardelegate =function(client,delegation){

        return {

            buttonWasPressed:function(){

                return delegation.buttonWasPressed.apply(client,arguments);

            }

        };

    };

    varFSM = {

        off: {

            buttonWasPressed: function () {

                console.log("关灯");

                this.currState =this.onState;

            }

        },

        on: {

            buttonWasPressed: function () {

                console.log("开灯");

                this.currState =this.offState;

            }

        }

    };

    varLight =function () {

        this.offState = delegate(this,FSM.off);

        this.onState = delegate(this,FSM.on);

        this.currState = FSM.off;

        this.button =null;

    };

    Light.prototype.init =function () {

        varbutton = document.createElement("button"), self =this;

        button.innerHTML = "开关";

        this.button = document.body.appendChild(button);

        this.button.onclick =function () {

            self.currState.buttonWasPressed.call(self);

        };

    };

    varlight =new Light();

    light.init();

})()

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

推荐阅读更多精彩内容

  • 恋人松开手 圆环遗落在草丛 在打完招呼后 不再回头 他不是鱼 她不是水 相去也不必多伤悲 悬空的手不再挽留 他们说...
    伍丁零阅读 154评论 0 6
  • 金鲳鱼 为热带肉食鱼类,海水性经济鱼类,仅在我国东南沿海地区有养殖,现广东、广西、海南和福建南部沿海是金鲳鱼的主产...
    b9173cba9a70阅读 365评论 0 1
  • 2017这一年 哭过,笑过 爱过,纠结过 当然,也崩溃过 也收获了的感动 是我太过矫情 所以时常感叹、沉沦 以及忧...
    唯天V阅读 229评论 3 1
  • 我想写关于社群学习的,我觉得我也算是社群学习者吧,从12年关注罗辑思维,到最近两年不断的开始参加线上课程去学...
    播新_怡睿阅读 228评论 1 5