时间:2023-03-05 12:21:18 | 栏目:JavaScript代码 | 点击:次
所谓命令,也就是指执行某些特定事情的指令,就拿喝水的例子来说,喝水执行的指令就是将水倒在杯子里,然后端起杯子送入口中,这就是一条命令,无论谁喝水都是这个步骤,我们不关心是谁端起了杯子,也不关心杯子中的水到底是水还是其他东西。我们只关心这个过程,将水倒在杯子中,然后端起杯子送入口中,最后谁喝掉了,喝掉的是饮料还是水不重要。
大家可能听的有点混,而命令模式最常见的应用场景就是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时我们用一种松耦合的方式来设计程序,使得请求发送者和接收者能够消除彼此间的耦合关系。
在实际开发中团队协作是最重要的环节,假如两个人分别得到两个不同的任务,一个人负责页面中所有的 button 按钮的美化,另一个人负责实现这些 button 的逻辑,我们在写 button 逻辑时不知道该 button 最终绑定的是页面中的哪一个按钮,也不知道具体点击该按钮会发生什么,此时我们用命令模式来设计,来解开按钮和负责具体行为对象间的耦合,代码如下所示。
<script> let btns = document.querySelectorAll('button'); // 写一个刷新命令 let refreshCommand = { execute () { console.log("刷新页面"); } } // 写一个登录按钮命令 let loginCommand = { execute () { console.log("执行登录操作"); } } let setCommand = function( button, command) { button.onclick = function() { command.execute(); } } // 假如第三个按钮用来负责刷新页面,第五个用来负责登录操作 setCommand( btns[2], refreshCommand); setCommand( btns[4], loginCommand);
分析:我们将点击按钮后可能会发生的事件分别封装起来,它们中各自有自己的 execute 函数,我们将安装按钮功能的函数封装到了 setCommand 中,最后根据需求对应安装各个按钮的功能便可。
下达一个命令后,我们可能不需要了,想回到下达命令之前的状态,就需要用到撤销操作,撤销操作的实现一般是给命令对象增加一个 undo 或者 unexecute 方法,在其中执行 execute 的反向操作。
// 假如写一个人物向左移的命令 let moveLeftCommand = { execute() { console.log("人物向左移动"); } undo (){ console.log("人物向又移动"); } }
这种是简单的逻辑实现,假如有些撤销行为无法用执行他的反向操作来实现,那我们应该怎么办呢?
带着这个问题我们学习一种新的达到撤销的方式,我们用一个栈来记录每一步命令,想要撤销,我们只需要重头开始,从栈中拿出命令来分别执行,将最后一次命令删除即可。如果不删除最后一次命令依次执行,我们又实现了一个新的功能,重播。
这是一个通过 WASD 来控制小球移动的代码实现,我们每次按下一个有效命令时,便会将该命令推入 commandStack 中来记录小球的变化,当点击重播时小球会按照之前同样的轨迹运动。
let role = document.getElementById('role'); let move = { dom: role, leftt:0, top: 0, up() { console.log("向上"); this.top -= 10; this.dom.style.top = this.top + 'px'; }, down(){ console.log("向下"); this.top += 10; this.dom.style.top = this.top + 'px'; }, left() { console.log("向左"); this.leftt -= 10; this.dom.style.left = this.leftt + 'px'; }, right() { console.log("向右"); this.leftt += 10; this.dom.style.left = this.leftt + 'px'; } } commands = { "119": "up", // W "115": "down", // S "97": "left", // A "100": "right" // D }; commandStack = []; // 保存命令的堆栈 let setCommand = function (receiver, state) { return function() { receiver[state](); } } document.addEventListener('keypress', (e) => { let code = e.charCode; if(!commands[code]) { return ; } let command = setCommand(move, commands[code]); if( command ) { command(); commandStack.push( command ); } }) // 设置重播按钮 document.getElementById('replay').onclick = function() { let command; while(command = commandStack.shift()) { command(); } }
宏命令就是一组命令的集合,通过执行宏命令可以一次执行一批命令。
通俗来说就是,有一件事你需要重复做,并且做这件事的每一步都是固定不变的,那么我们可以将做这件事情的所有步骤打包起来,只需要启动,便可以自动完成所有步骤。
需求:实现一个游戏自动刷副本的脚本
? let MacroCommand = function() { return { commandList: [], add( command ) { this.commandList.push(command); }, execute () { for(let i = 0,command; command = this.commandList[i++];){ command.execute(); } } } } let startGameCommand = { execute() { console.log('开始游戏') } } let killGhostCommand = { execute() { console.log('打怪') } } let giftCommand = { execute() { console.log("领取奖励") } } let outGameCommand = { execute() { console.log("退出游戏") } } let Game = MacroCommand(); Game.add(startGameCommand); Game.add(killGhostCommand); Game.add(giftCommand); Game.add(outGameCommand); Game.execute();
我们只需要为宏命令对列中添加命令,它会自动执行里边的所有命令。我们只负责启动,其他的就不需要我们管了,我们可以放心的去做其他事情,等待它自己完成。
命令模式中也提到了傻瓜命令和智能命令,它两的区别在于傻瓜命令中含有命令的接收者,而智能命令没有,它直接提供请求。智能命令与策略模式十分相近,只是在使用上不同,策略模式中对象的目标相同,只是实现目标的算法不同,而命令模式的目标更具有散发性。命令模式还可以完成撤销、排队的功能。