如果把设计模式当做一门语言的话,我觉得组合模式就是里面的数组,或者更确切的说,应该算是一棵树。 树的枝干就是其他模式的使用,比如命令模式,代理模式,单例模式等等。总的来说,组合模式在这里就相当于一个容器,但也并非仅仅是个容器(不然,我还给他冠名"模式"). 组合模式+命令模式
上篇的命令模式大家应该可以了解到,一个命令和命令执行者的相关系数为 zero . 所以你对命令的执行者做什么都不要紧,但是他的接口必须保持一致。 而这个特性映射出一个道理,就是既然我可以do anything, 意味着我可以在执行者上面加上其他的执行者. //当点击一个球后,让球往上走,再往右走 var setCommand = function(ele, command) { ele.onclick = function() { command.exe(); } } var locat = (function() { //执行事件类 var ball = getEle("#pic"), style = ball.style; //缓存style var move = function(direct) { return function() { var dir; switch (direct) { case "bottom": dir = parseInt(getLocat(ball).top) || 0; style.top = `${dir+10}px`; break; case "right": dir = parseInt(getLocat(ball).left) || 0; style.left = `${dir+10}px`; break; default: dir = parseInt(getLocat(ball)[direct]) || 0; style[direct] = `${dir-10}px`; } } } return { moveUp: move("top"), moveDown: move("bottom"), moveLeft: move("left"), moveRight: move("right") } })(); //封装命令 var moveUR = (function(){ var exe = function(){ locat.moveUp(); locat.moveRight(); } return { //封装命令 exe } })(); setCommand(getEle("leftUpBtn"), moveUR); //绑定执行效果的程序
可以看出,上面的代码可以完成基本任务。但是,如果某一天你的leader要求,让小球 上->右->下->左 进行一个循环回到原始的位置,恐怕你的反应不是很强烈。就是在moveUR里面再加些东西呗。呵呵,来,你leader又说了。在上->右->下的时候转个圈然后->左.(Ps: 转个毛线啊~~~~), 表示听到这里,想想某天你的leader抽风了,又换一个实现方式。 我想,你年都过不好~。 //上面部分不动,只将下面部分改写 //命令集合 var command = function(){ var lists = []; return { add(command){ lists.push(command); }, exe(){ for(var i = 0,command;command = lists[i++];){ command.exe(); } } } } //封装命令 var moveUp = (function(){ var exe = function(){ locat.moveUp(); } return { exe } })(); var moveLeft = (function(){ var exe = function(){ locat.moveLeft(); } return{ exe } })(); var moveGroup1 = command(); //得到一个命令集 moveGroup1.add(moveUp); //添加向上的命令 moveGroup1.add(moveLeft); //。。。。 setCommand(getEle("leftUpBtn"), moveGroup1); //给向上的button,绑定向上的执行程序
可以看出,使用组合模式+命令模式可以完美的打出4个2+两个王的终极炸弹。 你可以随机的添加你想要的效果,但是前提还是必须保证有一致的接口内容。
var moveUP = (function(){ var exe = funciton(){ conosle.log("moveUP"); } return { exe } })(); var moveLeft = (function(){ var exe = funciton(){ conosle.log("moveLeft"); } return { exe } })(); var moveBottom = (function(){ var exe = funciton(){ conosle.log("moveBottom"); } return { exe } })(); var moveRight = (function(){ var exe = funciton(){ conosle.log("moveRight"); } return { exe } })(); //加一点难度,对命令进行分组 var moveGrop1 = command(); moveGrop1.add(moveUp); //moveUp moveGrop1.add(moveLeft); //moveLeft var moveGrop2 = command(); moveGrop2.add(moveBottom); //moveBottom moveGrop2.add(moveRight); //moveRight var moveGrop3 = command(); moveGrop3.add(moveBottom); //moveBottom moveGrop3.add(moveBottom); //moveBottom var moveGrop = command(); moveGrop.add(moveGrop1); moveGrop.add(moveGrop2); moveGrop.add(moveGrop3); moveGrop.exe(); //接下来会按上面的顺序一个一个的运行。
可以看出,组合模式最大的一个特点就是分组操作。将同类操作(同目的操作)放在一组。 就像做一个gif了,一个帧+一个帧+一个帧... 比如,我可以蹲下来,然后起跳。 我又可以蹲下来,然后站立,走路。 就可以把一个片段,一个片段组合成你最想要的效果(怀念高中时候没有好好学习排列组合 :(] 而且重用性也是杠杠的。 //命令集合 var command = function(){ var lists = []; return { add(){ for(var i = 0,command; command = arguments[i++];){ lists.push(command); } console.log(lists); }, exe(){ for(var i = 0,command;command = lists[i++];){ command.exe(); } } } } //添加命令 var moveGrop1 = command(); moveGrop1.add(moveUp,moveLeft); //moveUp,moveLeft var moveGrop2 = command(); moveGrop2.add(moveBottom,moveRight); //moveBottom,moveRight var moveGrop3 = command(); moveGrop3.add(moveBottom,moveBottom); //moveBottom,moveBottom var moveGrop = command(); moveGrop.add(moveGrop1,moveGrop2,moveGrop3); moveGrop.exe(); //接下来会按上面的顺序一个一个的运行。
干净,漂亮,美美的. var moveGrop = command(); moveGrop1.add(moveUP); moveUP.add(moveLeft);
虽然看上去这段代码很蠢,但事实上,我确实踩过===我也很蠢xxx... 所以,这里希望警戒大家,希望把这个错误的发生率降到 0.000000001%.吧。 var moveUp = (function(){ var exe = funciton(){ conosle.log("moveUp"); } var add = function(){ throw "你很蠢,不,你真的很蠢." } return { exe,add } })(); 这样,万一哪天,真的蠢了,好歹也知道自己怎么蠢的。自己知道自己蠢就可以了,记得代码写完后,把这段给删了(红脸). 组合模式注意tips组合模式虽然很strong.但是有些概念性问题,我们还是必须分清楚.
组合模式的其他用处
这个用处出自于< AlloyTeam的曾探\>.(一枚我超级崇拜的大神) var folder = function(name){ return { name, //文件夹名 files:[], add(){ for(var i = 0,file;file = arguments[i++];){ file.parent = this; this.files.push(file); } }, scan(){ //扫描文件 for(var file of this.files){ console.log(file.name); } }, remove(){ if(!this.parent){ alert("该文件是更目录,不能删除!"); return; } for(var files = this.parent.files,len = files.length-1;len>=0;len--){ //倒叙遍历文件夹 var file = files[len]; if(file === this){ files.splice(len,1); //删除文件 break; } } } } } var file = function(name){ return { name, scan(){ console.log(`this is ${name}`); }, remove(){ if(!this.parent){ alert("该文件是更目录,不能删除!"); return; } for(var files = this.parent.files,len = files.length-1;len>=0;len--){ //倒叙遍历文件夹 var file = files[len]; if(file === this){ files.splice(len,1); //删除文件 break; } } }, add(){ throw "你很蠢,不,你真的很蠢!" } } } var jimmyFolder = folder("jimmy"); var avi1 = file("小泽玛利亚.avi"); var avi2 = file("上野真知子.avi"); jimmyFolder.add(avi1,avi2); jimmyFolder.scan(); //小泽玛利亚.avi , 上野真知子.avi avi1.remove(); jimmyFolder.scan(); //上野真知子.avis
恩,get(工口). |