目录
  • 相关定义:
  • 自我理解:
  • 解耦体现:
  • 代码举例1:
  • 代码举例2:
    • 使用命令设计模式可以灵活组合网络请求,如下所示:
  • 代码举例3:
    • 使用命令设计模式实现撤销和重做,用到了栈数据结构,如下所示:
  • 命令设计模式和策略设计模式的不同:
    • 原生使用
      • 业务实践:

        相关定义:

        • 使用命令模式,可以将【请求的调用者】和【请求的执行者】解耦。
        • 调用者通过【持有命令对象】来【间接调用】接收者的方法,而无需【直接引用】接收者或了解其【具体实现】。
        • 这种解耦使得我们能够更加灵活地【扩展】和【改变】命令的调用方式。
        • 例如,我们可以【将命令对象保存在【队列中】】,实现命令的【排队】和【异步执行】。
        • 还可以记录命令的【历史,以支持撤销和重做】操作。
        • 命令模式应用场景,【菜单操作】、【多级撤销】、【批处理任务】等。

        自我理解:

        • 执行者对象E:普通对象,提供了整个流程中的执行方法,此方法对于具体的命令是可感知的,对于调用者是无感知的!
        • 抽象命令类A:规定了具体命令的基本结构,为调用者提供统一的调用接口。
        • 具体命令类S:是对抽象命令类的实现,核心是封装了执行者,准确来说命令只封装了执行者的部分方法;抽象方法的执行本质上是执行了这些方法。
        • 调用者对象C:封装了两个方法和一个属性;属性表示的是当前命令,第一个方法是设置/切换命令具体值,第二个方法是执行此方法。
        • S封装了E,C只能接触到S,所以C和E是解耦的
        • A和S的关系是一对多

        解耦体现:

        • C无需了解S具体内容,只需完成触发 ;
        • S无需知道E怎么完成任务,只需调用E暴露出来的方法即可;
        • S和E之间是多对多的关系,即一个S可以由多个E的部分方法组合完成,一个E也可以被不同的S封装其上不同的部分方法;
        • C维护的不只是一个S,还可以是一个S队列,当触发到来的时候,S队列依次执行。

        代码举例1:

        // 接收者对象 class Light {     turnOn() {}     turnOff() {} } // 命令接口 abstract class Command {     abstract execute():void; } // 具体命令:打开灯 class TurnOnCommand extends Command {     constructor(public light: Light) { super() }     execute() { this.light.turnOn() } } // 具体命令:关闭灯 class TurnOffCommand extends Command {     constructor(public light: Light) { super() }     execute() { this.light.turnOff() } } // 调用者对象 class RemoteControl {     command: Command;     setCommand(command: Command) { this.command = command }     pressButton() { this.command.execute() } } // 使用示例 // 创建执行者 const light = new Light(); // 创建具体命令对象,封装执行者 const turnOnCommand = new TurnOnCommand(light); const turnOffCommand = new TurnOffCommand(light); // 创建调用者对象 const remoteControl = new RemoteControl(); // 设置具体命令对象然后执行 remoteControl.setCommand(turnOnCommand); remoteControl.pressButton();

        代码举例2:

        使用命令设计模式可以灵活组合网络请求,如下所示:

        // 请求接收者 class Reciever {   async get(path: string) {     const res = await fetch(`https://rec.example.com/${path}`);     const data = await res.json();   } } // 命令接口 class Cmd { exe() {} } // 具体命令:发送请求 class RCd extends Cmd {   constructor(public rec, public url) {     super();   }   exe() {     return this.rec.get(this.url);   } } // 调用者对象 class RM {   cQueue: Cmd[] = [];   add(command: Cmd) {     this.cQueue.push(command);   }   pReq() {     const promises = this.cQueue.map((command) => command.exe());     return Promise.all(promises);   } } // 使用示例 const rec = new Reciever(); // 创建请求接收者 const rM = new RM(); // 创建请求管理者 // 添加具体请求命令 rM.add(new RCd(rec, 'data1')); rM.add(new RCd(rec, 'data2')); rM.add(new RCd(rec, 'data3')); rM.pReq()   .then(() => {     console.log('所有请求已完成');   })   .catch((error) => {     console.error('请求出错:', error);   });

        代码举例3:

        使用命令设计模式实现撤销和重做,用到了栈数据结构,如下所示:

        // 命令接口:想要用命令策略实现撤销、重做就必须先在抽象接口中定义好撤销的接口 abstract class Cmd {   abstract exe(): void;   abstract undo(): void; } // 具体命令类 - 加法命令 class AddCmd extends Cmd {   constructor(public rec: Rec, public value: number) {     super();   }   exe() {     this.rec.add(this.value);   }   undo() {     this.rec.subtract(this.value);   } } // 接收者类 class Rec {   result = 0;   add(value: number) { this.result += value }   subtract(value: number) { this.result -= value } } // 调用者/发送者 class Invoker {   cmds: Cmd[] = [];   xcmd: Cmd[] = [];   exe(cmd: Cmd) {     cmd.exe();     this.cmds.push(cmd);   }   // 重点   undo() {     const cmd = this.cmds.pop();     if (!cmd) return;     cmd.undo();     this.xcmd.push(cmd);   }   // 重点   redo() {     const cmd = this.xcmd.pop();     if (cmd) {       cmd.exe();       this.cmds.push(cmd);     }   } } // 示例用法 const rec = new Rec(); // 创建接收者对象 const ivk = new Invoker(); // 创建调用者对象 const addCmd = new AddCmd(rec, 5); // 创建加法命令 ivk.exe(addCmd); // 执行加法命令,结果为:5 ivk.undo(); // rec.result = 0 ivk.redo(); // rec.result = 5

        命令设计模式和策略设计模式的不同:

        • 命令设计模式的最小操作单元是【命令对象】;而策略设计模式的最小操作单元是方法,或者算法。
        • 命令设计模式一次只操作一个命令对象;而策略设计模式为了完成任务可以组合多个策略。
        • 命令设计模式一般不会将某个命令单独保存到内部状态中;而策略设计模式必须保存当前的策略。
        • 使用命令设计模式可以实现撤销、重做等功能、这反映出各个命令之间是平等关系;而策略设计模式的各个策略之间可能是先后顺序关系。

        原生使用

        下面这些是 JavaScript 中常见的原生部分,它们在某种程度上使用到了命令模式的思想和机制。通过封装行为成具体的对象并在需要时进行调用,这些原生功能可以提供更灵活、可扩展的方式来处理相关的请求或操作。

        • 事件处理:JavaScript 中的事件处理机制可以看作是一种命令模式的应用。当用户与页面进行交互时,例如点击按钮、键盘按键或鼠标移动等,事件被触发并执行相应的处理函数。这里事件就充当了命令对象,而事件处理函数则扮演着命令的接收者。
        • XMLHttpRequest 对象:在早期的 Ajax 开发中,我们常使用 XMLHttpRequest 对象来进行异步请求。开发者可以将每个请求封装成一个对象,并通过调用 send() 方法来发送请求。这里的 XMLHttpRequest 对象和 send() 方法即可看作是命令模式的实现,发送请求的行为被封装成具体的命令对象。
        • History API:浏览器的 History API 提供了对浏览器历史记录的控制。通过调用 pushState() 或 replaceState() 方法,我们可以添加或替换浏览器的历史记录条目,并关联相应的状态数据。这里的 pushState() 和 replaceState() 方法可以看作是命令对象,用于执行添加或替换历史记录的操作。
        • document.execCommand():Document 对象的 execCommand() 方法允许在网页中执行命令式的编辑操作,如粘贴、剪切、加粗、斜体等。开发者可以调用 execCommand() 方法并传递相应的命令参数来执行这些操作,从而实现富文本编辑功能。
        • setTimeout() 和 setInterval():JavaScript 提供了 setTimeout() 和 setInterval() 函数来实现定时器功能。开发者可以使用这两个函数将一段代码封装成一个函数对象,并在指定的时间间隔后执行相应的代码,相当于将定时器行为封装成具体的命令对象。

        业务实践:

        • 按钮和用户交互:当你需要实现一个具有撤销、重做或记录操作历史的按钮交互功能时,可以使用命令模式。每个按钮可以表示一个命令对象,按下按钮时执行相应的命令操作。

        也就是说但凡见到按钮,都可以使用命令设计模式。

        • 异步请求管理:当你需要对异步请求进行批处理、队列化或延迟执行时,命令模式可以很好地组织和管理这些请求。将每个请求封装成一个命令对象,并使用命令队列来依次执行这些命令。
        • 菜单和快捷键:当你需要实现复杂的菜单系统或支持快捷键操作时,命令模式可以帮助你处理不同的菜单项或快捷键动作。每个菜单项或快捷键可以关联一个命令对象,触发时执行相应的命令操作。
        • 动画控制:当你需要控制页面元素的复杂动画序列或状态切换时,命令模式可以提供一种有效的方式。每个动画或状态切换可以封装成一个命令对象,通过调用者来触发执行。
        • 历史记录与撤销:当你需要实现撤销和重做功能或记录用户操作历史时,命令模式非常有用。每个用户操作可以表示一个命令对象,并在执行时更新状态或记录操作,以便支持撤销和重做操作。

        以上就是JS设计模式之命令模式的用法详解的详细内容,更多关于JS命令模式的资料请关注本网站其它相关文章!

        您可能感兴趣的文章:

        • 详解Javascript实践中的命令模式
        • JavaScript设计模式之命令模式
        • javascript设计模式 – 命令模式原理与用法实例分析
        • JavaScript命令模式原理与用法实例详解
        • JS设计模式之命令模式概念与用法分析
        • JS命令模式例子之菜单程序