命令模式

72 阅读1分钟

命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和接受者之间能够消除彼此之间的耦合关系。

实现

数据结构

  • receiver: Map<key, action> , 表示接受者,通过接收对应的 key 来执行指令
  • keyMap : Map<event, key> , 表示 key 的映射, 将具体的事件映射到 key 上 ,得到对应的key
  • makecommand: function( recevier , key ) => commond:function , 从 recevier 中取出对应的action , 得到指令
  • commondStack : Stack<command> , 存储命令的执行栈,方便进行撤回、回放、重做等操作。

书中用《街头霸王》游戏的回放功能的一个例子

<!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="replay">播放录像</button>
    
    <script>
      // 接受者 | 动作集
      let Ryu = {
        attack: function () {
          console.log("攻击");
        },
        defence: function () {
          console.log("防御");
        },
        jump: function () {
          console.log("跳起");
        },
        crouch: function () {
          console.log("蹲下");
        },
      };

      // 中间层,将接受者和命令结合起来,形成可执行命令
      let makeCommand = function (receiver, state) {
        return function () {
          receiver[state](); // 在执行匿名函数的时候就执行里面的命令
        };
      };

      // 命令集
      let commands = {
        119: "jump", // W
        115: "crouch", // S
        97: "defence",
        100: "attack",
      };

      let commandStack = []; // 记录执行栈

      document.onkeypress = function (ev) {
        let keyCode = ev.keyCode;
        let command = makeCommand(Ryu, commands[keyCode]);
        if (command) {
          command();
          commandStack.push(command);
        } else {
          return;
        }
      };

      let replayBtn = document.getElementById("replay");

      replayBtn.onclick = function () {
        commandStack.forEach((command) => command());
      };
    </script>
  </body>
</html>

除了撤销,我们还可以通过操作命令栈来完成排队、组合等功能。

跟许多其他语言不同,JavaScript 可以用高阶函数非常方便地实现命令模式。命令模式在JavaScript 语言中是一种隐形的模式。