转载

我说你做–命令模式

命令模式

望文生义,所谓的命令模式其实就是:

发出一定的指令,然后由对象接受并且执行

需要强调一点,就是对于命令的发出者来说,他并不知道该命令是给谁,执行效果是怎样,他只管发出命令就行。听到这里感觉和发布订阅者模式有异曲同工的效果。 但事实上,他们两者应用的场景还是有比较大的区分。不仅写法上有不同,而且执行的过程也有所不同。要知道在命令模式里面执行的效果是1对1,而在订阅者模式里面是1对>=1的.

你在逼逼什么?

哦,说明白一点。 就和上课一样。 老师进教室了,首先说:“上课!”. 接着,你们的monitor 会立马接上: “起立!”。 然后,你们就会异口同声的说:”老湿好~”。 没错,分析一下。 当老师说上课的时候,他并不会知道谁会说起立,比如今天班长谈恋爱去了,那副班长顶上。 而且,说完起立之后,副班长也不知道谁会说 老湿好 。 也就是命令的发出者,只管发出一个命令,然后你们只管执行就over了.

talk is cheap, show u code.

//事件发出者 var setCommand = function(ele,command){ //命令的绑定者     ele.onclick = function(){         command.do();     } } //事件的执行者  var location = (function() { //执行事件类     var ball = getEle("#ball");     var move = function(direct) {         return function() {             var style = ball.style,                 dir = parseInt(style[direct]);             style[direct] = `${dir-200}px`;         }     }     var moveUp = move("top");     var moveDown = move("down");     var moveLeft = move("left");     var moveRight = move("right");     return {         moveUp,          moveDown,         moveLeft,         moveRight     } })(); //封装命令 var MoveUp = function(exer){     this.exer = exer; } MoveUp.prototype.do = function(){     this.exer.moveUP(); } var MoveDown = function(exer){     this.exer = exer; } MoveDown.prototype.do = function(){     this.exer.moveDown(); } var MoveLeft = function(exer){     this.exer = exer; } MoveLeft.prototype.do = function(){     this.exer.moveLeft(); } var MoveRight = function(exer){     this.exer = exer; } MoveRight.prototype.do = function(){     this.exer.moveRight(); } setCommand(getEle("upBtn"), new MoveUp(location)); //给向上的button,绑定向上的执行程序 setCommand(getEle("downBtn"), new MoveDown(location)); //... setCommand(getEle("leftBtn"), new MoveLeft(location)); //... setCommand(getEle("rightBtn"), new MoveRight(location)); //.... 

可以清晰的看到,在命令模式中,触发事件(onclick)和执行程序(command.do())都是已知的。 但是这个执行的消息给谁,或者执行产生的效果是怎样的,在命令的发出者这一方都是未知的。需要注意的是,这时候的未知只限于命令的发出者而言。也就是现在命令模式将发出者和执行者给解耦开,即,可变的部分和不可变的部分分开。

上面逼逼这么多到底在说shenme…

其实一切原理都是枯燥的,实例才是王道。 来,我们来做个比较。也就是不使用命令模式,直接写上面的例子(偷个懒,只写moveUP部分).

var ele = getEle("#ball"); getELe(".moveUp").onclick = function(){      var style = this.style,         dir = parseInt(style["top"]);         style["top"] = `${dir-200}px`;     } }

上面的代码同样能完成上面 辣么辣么 长的代码完成的效果,那为什么还要使用上面的写法呢?

艹~ 请问,你下面那段代码,能体现你的bigger吗? 能体现你是代码艺术家的feeling吗?能体现你的思维能力吗?

No NO No~

我们来分析下why.

首先下面那段代码可以完成上面的功能,但是万一有一天,一个名叫产经的生物和你说

"亲爱的,你能不能在加一个button,让这个球可以斜着走,可以转个圈呢? 哈哈,我相信你一定可以的。" 

呵呵,你话都没说。 想当然这个锅,你必须背。好吧,那开始做吧。(用那个渣渣代码演示一遍).

function getY(x){     var k = 1.2;     return k*x; } getELe("#diagnoal").onclick = function(){      var style = ele.style,         x = parseInt(style['left']),         y = parseInt(style['top']),         style['left'] = `${x-200}px`;         style["top"] = `${y-getY(200)}px`;     } }

可以想象,最后如果产经的需求不断增多,那么你在事件处理的回调会越来越复杂,比如:

"亲爱的,你斜着走都实现了,那4个方向能不能都可以走呢?"

我想这时候,你应该会懵逼了。不要紧,我们可以使用命令模式来弥补这个缺陷,因为命令模式最大的一个扩展性就是命令者和命令的执行者分开了。而且在上面面向过程的代码中,看不出什么逻辑出来,只是知道,这个click是触发什么的。 而事件回调中的代码重用性也是非常低的。

这里使用命令模式重构一遍

//其他的还是一样,这里主要将4个方向的代码重构一下

var location = (function() { //执行事件类     var ball = getEle("#ball");     var compMove = function(hori,vert) { //垂直和水平方向         var k = 1.2; //移动的斜率         var getY = function() {             return k * x;         }         return function() {             var style = ball.style,                 x = parseInt(style[hori]), //水平方向上的位置                 y = parseInt(style[vert]); //垂直方向上的位置             style[hori] = `${x-200}px`;             style[vert] = `${y-getY(200)}px`;  //执行移动         }     };     //斜方向绑定代码     var moveLU = compMOve("left","top");      var moveRU = compMOve("right","top");     var moveLB = compMOve("left","bottom");     var moveRB = compMOve("right","bottom");     return {         moveLU         moveRU,         moveLB,         moveRB     } })(); //封装命令 var MoveLU = function(exer) {     this.exer = exer; } MoveLU.prototype.do = function() {     this.exer.moveLU(); }    setCommand(getEle("leftUpBtn"), new MoveLU(location)); //给向上的button,绑定向上的执行程序

可以看出来,虽然代码多,但是至少我们将改动的地方降到最低了。

setCommand这个不变,变的只是绑定click的对象和执行者。 这样可以清楚的说明命令模式的优势到底在哪里。

当然,我们还可以做一个优化,要知道,js是一门函数至上的语言,因为函数可以像参数一样被传来传去,所以可以这样改写命令的绑定者.

var setCommand  = function(ele,fn){     ele.onclick = function(){         fn();     } } setCommand(getEle("leftUpBtn",()=>{location.moveLU()}))); //给向上的button,绑定向上的执行程序

这样就可以省去中间一大堆的事件修饰,从而将函数直接暴露使用。推荐这样写法,因为这个才是js的真正实力。要知道一个模式的精华不是看他能怎么用,而是要看你怎么用他。

命令模式实现缓存效果

其实,缓存并不是什么高上大的东西,就是在函数里名,有一个变量来保存你的结果,而你可以遍历这个结果.

 function fb(num) {     if (num <= 1) {         return 1;     }     return num * fb(--num) } //缓存代理出场了 var cProxy = (function() {     var cache = {};     return function(num) {         if (cache[num]) {             console.log(`this is cache ${cache[num]}`);             return cache[num];         }         return cache[num] = fb(num);     } })(); //测试 console.log(cProxy(4)); //24 cProxy(4); //"this is cache 24"

上面是我以前写代理缓存的例子。 里面有个叫cache的东西,就是来保存你的结果(放在内存中),以备下次使用。

而命令模式的缓存有个极大的用途就是一个 撤销和重做的效果.

在上面的例子中可以保留每一个节点小球的位置(简单起见,还是以最初的上下左右为基准吧)

由于代码过长,我放在fiddle里面(里面代码可能会和上面有很大出入,但是如果理解了上面的说法的话,我相信理解起来肯定很快的)。有兴趣可以看看。是个实例demo哦。 我说你做–命令模式

撤销实例

特此说明,由于使用原生写的里面会有写hacks,如果大家有自己独到的见解,欢迎拍砖(请轻点~). 也欢迎点个推荐呗。

原文  http://hao.jser.com/archive/9132/
正文到此结束
Loading...