# 命令模式

命令模式(Command Pattern)是一种行为设计模式,它将请求封装成对象,从而使你可以用不同的请求对客户进行参数化。这个模式也支持可撤销的操作。

# 命令模式的主要角色

  • 命令接口(Command Interface):定义了一个执行操作的接口。
  • 具体命令类(Concrete Command Class):实现了命令接口,并持有接收者对象的引用,负责调用接收者的相应方法来执行请求。
  • 接收者(Receiver Class):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
  • 调用者(Invoker Class):要求该命令执行这个请求。
  • 客户端(Client):创建一个具体命令对象并设定它的接收者。

# 示例

以下是一个使用命令模式的 JavaScript 编辑器示例:

// 接收者类(编辑器)
class Editor {
  constructor() {
    this.content = [];
    this.history = [];
    this.future = [];
  }

  insert(text, position) {
    if (position === null || position < 0) {
      this.content.push(...text.split(''));
    } else {
      const before = this.content.slice(0, position);
      const after = this.content.slice(position);
      this.content = [...before, ...text.split(''), ...after];
    }
    this.history.push({ type: 'insert', text, position });
    this.future = [];
  }

  remove(position, length) {
    const before = this.content.slice(0, position);
    const after = this.content.slice(position + length);
    const removedText = this.content.slice(position, position + length).join('');
    this.content = [...before, ...after];
    this.history.push({ type: 'remove', position, length, text: removedText });
    this.future = [];
  }

  getContent() {
    return this.content.join('');
  }
}

// 具体命令类(插入文本命令)
class InsertCommand {
  constructor(editor, text, position) {
    this.editor = editor;
    this.text = text.split('');
    this.position = position;
  }

  execute() {
    this.editor.insert(this.text.join(''), this.position);
  }

  undo() {
    this.editor.remove(this.position, this.text.length);
  }
}

// 具体命令类(删除文本命令)
class RemoveCommand {
  constructor(editor, position, length) {
    this.editor = editor;
    this.position = position;
    this.length = length;
  }

  execute() {
    this.editor.remove(this.position, this.length);
  }

  undo() {
    this.editor.insert(this.text, this.position);
  }
}

// 调用者类(编辑器界面)
class EditorInterface {
  constructor(editor) {
    this.editor = editor;
  }

  insert(text, position) {
    const command = new InsertCommand(this.editor, text, position);
    command.execute();
  }

  remove(position, length) {
    const command = new RemoveCommand(this.editor, position, length);
    command.execute();
  }

  undo() {
    if (this.editor.history.length === 0) return;
    const action = this.editor.history.pop();
    if (action.type === 'insert') {
      const command = new RemoveCommand(this.editor, action.position, action.text.length);
      command.undo();
    } else if (action.type === 'remove') {
      const command = new InsertCommand(this.editor, action.text, action.position);
      command.undo();
    }
  }

  redo() {
    // 可以在这里实现重做逻辑
  }
}

// 客户端代码
const editor = new Editor();
const editorInterface = new EditorInterface(editor);

editorInterface.insert('Hello, ', 0); // 输出: Hello, 
editorInterface.insert('World!', 13); // 输出: Hello, World!
console.log(editor.getContent()); // 输出: Hello, World!

editorInterface.undo(); // 输出: Hello, 
console.log(editor.getContent()); // 输出: Hello, 

editorInterface.redo(); // 输出: Hello, World!
console.log(editor.getContent()); // 输出: Hello, World!

# 命令模式的优势和适用场景

优点:

  • 将请求的发起者和接收者解耦,使得代码更灵活和可扩展。
  • 支持撤销和重做操作。
  • 可以将命令记录在日志中,便于追踪和审计。

适用场景:

  • 需要将请求封装成对象,并延迟执行或进行参数化。
  • 需要支持队列和日志功能。
  • 需要实现可撤销的操作。
  • 需要解耦调用者和接收者之间的关系。

# 总结

通过使用命令模式,可以将请求的发起者和接收者解耦,并通过一个中介者(即命令对象)来传递请求。这使得代码更灵活、可扩展,并且支持撤销和重做操作等高级功能。