命令模式
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
传统的命令模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="button1">点击按钮1</button>
<button id="button2">点击按钮2</button>
<button id="button3">点击按钮3</button>
<script>
const button1 = document.getElementById("button1");
const button2 = document.getElementById("button2");
const button3 = document.getElementById("button3");
const MenuBar = {
refresh() {
console.log("刷新菜单目录");
},
};
const SubMenuBar = {
add() {
console.log("增加子菜单");
},
del() {
console.log("删除子菜单");
},
};
function setCommand(el, command) {
el.onclick = function () {
command.execute();
};
}
// 命令对象类,包含execute方法
// receiver 实际执行动命令的接收者对象
// key 指示接收者对象里方法名
class MenuBarCommand {
constructor(receiver, key) {
this.receiver = receiver;
this.key = key;
}
execute() {
this.receiver[this.key]();
}
}
setCommand(button1, new MenuBarCommand(MenuBar, "refresh"));
setCommand(button2, new MenuBarCommand(SubMenuBar, "add"));
setCommand(button3, new MenuBarCommand(SubMenuBar, "del"));
</script>
</body>
</html>
JavaScript版本使用闭包和高阶函数实现带有历史记录的命令模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>命令模式-街头霸王</title>
</head>
<body>
<button id="replay">播放录像</button>
<script>
const Ryu = {
attack() {
console.log("攻击");
},
defense() {
console.log("防御");
},
jump() {
console.log("跳跃");
},
crouch() {
console.log("蹲下");
},
};
// 使用闭包创建命令
const makeCommand = function (receiver, state) {
return function () {
receiver[state]();
};
};
const commands = {
119: "jump", // W
115: "crouch", // S
97: "defense", // A
100: "attack", // D
};
// 保存历史命令记录
const commandStack = [];
document.onkeypress = function (ev) {
const keyCode = ev.keyCode;
const command = makeCommand(Ryu, commands[keyCode]);
if (command) {
commandStack.push(command);
command();
}
};
document.getElementById("replay").onclick = function () {
let command;
while ((command = commandStack.shift())) {
command();
}
};
</script>
</body>
</html>