前端设计模式 —— 命令模式

111 阅读3分钟

命令模式是一种行为设计模式,它允许将请求或操作封装成一个独立的对象,从而使你能够将客户端与具体的接收者解耦。该模式提供了一种抽象化的方式来处理命令,并使得能够将客户端的请求与接收者的实现进行分离,使得它们可以独立地变化。命令模式通过将命令请求封装成对象,可以实现命令的撤销、重做、记录日志等功能。在日常编程中,我们可以用命令模式来实现可撤销的操作、延迟执行等需求。

优缺点

命令模式的优点:

  1. 解耦合:将请求者和接收者解耦合,可以让命令发送者和接收者彼此独立,互不影响。请求者只需要知道如何发送命令,而不需要知道命令的具体执行过程。
  2. 可扩展性:可以很方便地添加新的命令,而不需要修改已有的代码。
  3. 支持撤销和恢复操作:可以通过保存命令历史记录的方式支持撤销和恢复操作。
  4. 支持事务:可以将多个命令封装为一个事务进行执行,保证多个命令的原子性。

命令模式的缺点:

  1. 代码量增加:实现命令模式需要创建多个类,这会增加代码的复杂度和维护成本。
  2. 可能带来性能问题:由于命令模式需要封装请求,可能会增加系统的调用次数,导致性能问题。

综上所述,命令模式适用于需要解耦合、可扩展性、支持撤销和恢复操作、支持事务等场景,但需要注意实现的复杂度和性能问题。

TypeScript 示例

interface Command {
  execute(): void;
  undo(): void;
}

class Light {
  private on: boolean = false;

  public switchOn(): void {
    this.on = true;
    console.log('The light is on.');
  }

  public switchOff(): void {
    this.on = false;
    console.log('The light is off.');
  }
}

class LightOnCommand implements Command {
  private light: Light;

  constructor(light: Light) {
    this.light = light;
  }

  public execute(): void {
    this.light.switchOn();
  }

  public undo(): void {
    this.light.switchOff();
  }
}

class LightOffCommand implements Command {
  private light: Light;

  constructor(light: Light) {
    this.light = light;
  }

  public execute(): void {
    this.light.switchOff();
  }

  public undo(): void {
    this.light.switchOn();
  }
}

class RemoteControl {
  private commands: Command[] = [];

  public setCommand(command: Command): void {
    this.commands.push(command);
  }

  public pressButton(index: number): void {
    if (this.commands[index]) {
      this.commands[index].execute();
    }
  }

  public undoButton(): void {
    if (this.commands.length > 0) {
      this.commands.pop().undo();
    }
  }
}

// Example Usage
const livingRoomLight = new Light();
const livingRoomLightOnCommand = new LightOnCommand(livingRoomLight);
const livingRoomLightOffCommand = new LightOffCommand(livingRoomLight);

const remoteControl = new RemoteControl();
remoteControl.setCommand(livingRoomLightOnCommand);
remoteControl.setCommand(livingRoomLightOffCommand);

remoteControl.pressButton(0); // The light is on.
remoteControl.pressButton(1); // The light is off.
remoteControl.undoButton(); // The light is on.

在上面的代码中,我们首先定义了 Command 接口,它有两个方法:execute()undo(),分别用于执行命令和撤销命令。然后我们定义了一个 Light 类,它表示灯的状态和行为。接下来,我们定义了两个 Command 类:LightOnCommandLightOffCommand,它们分别表示开灯和关灯的命令,并且在执行命令时会调用 Light 对象的相应方法。最后,我们定义了一个 RemoteControl 类,它维护了一个 Command 数组,并且有 setCommand()pressButton()undoButton() 方法,分别用于设置命令、执行命令和撤销命令。

在使用命令模式时,我们首先创建一个命令对象,并将其添加到 RemoteControl 对象中。然后,当我们需要执行命令时,只需要调用 RemoteControl 对象的 pressButton() 方法,它会调用相应的命令对象的 execute() 方法来执行命令。当我们需要撤销命令时,只需要调用 RemoteControl 对象的 undoButton() 方法,它会调用相应的命令对象的 undo() 方法来撤销上一个命令。

JavaScript 示例

// 接收者
class Light {
  turnOn() {
    console.log('灯亮了');
  }
  turnOff() {
    console.log('灯灭了');
  }
}

// 命令接口
class Command {
  execute() {}
}

// 具体命令:开灯命令
class TurnOnCommand extends Command {
  constructor(light) {
    super();
    this.light = light;
  }
  execute() {
    this.light.turnOn();
  }
}

// 具体命令:关灯命令
class TurnOffCommand extends Command {
  constructor(light) {
    super();
    this.light = light;
  }
  execute() {
    this.light.turnOff();
  }
}

// 调用者
class Invoker {
  constructor() {
    this.commands = [];
  }
  addCommand(command) {
    this.commands.push(command);
  }
  executeCommands() {
    for (let i = 0, len = this.commands.length; i < len; i++) {
      this.commands[i].execute();
    }
  }
}

// 使用
const light = new Light();
const turnOnCommand = new TurnOnCommand(light);
const turnOffCommand = new TurnOffCommand(light);
const invoker = new Invoker();
invoker.addCommand(turnOnCommand);
invoker.addCommand(turnOffCommand);
invoker.executeCommands(); // 灯亮了,灯灭了