跳至主要內容

命令模式

约 643 字

命令模式

核心思想

命令模式将请求封装为一个对象,将发出者和处理者解耦,并能延迟请求执行或放入队列中,实现可撤销操作。

典型用例

撤销重做

命令模式可以用于实现撤销重做功能,每个执行的操作都是一个命令对象,可以被存储和恢复。

在这个例子中,创建了一个简单的文本编辑器和一些命令来修改文本。CommandInvoker 类负责执行命令并维护历史记录,以便支持撤销和重做功能。这样可以灵活地添加新的命令类型,同时保持编辑器类的简洁性和命令的独立性。

// npm run code src/code/design-pattern/command/undo-redo.ts

export {};

// 定义命令接口
interface Command {
    execute(): void;
    undo(): void;
}

// 创建接收者执行与请求相关操作
class TextEditor {
    private content: string = '';

    append(text: string): void {
        this.content += text;
    }

    delete(): void {
        this.content = this.content.slice(0, -1);
    }

    getContent(): string {
        return this.content;
    }
}

// 创建具体命令类,每个命令类将执行一个操作并保存其状态以便撤销。
class AppendCommand implements Command {
    private editor: TextEditor;
    private text: string;

    constructor(editor: TextEditor, text: string) {
        this.editor = editor;
        this.text = text;
    }

    execute(): void {
        this.editor.append(this.text);
    }

    undo(): void {
        for (let i = 0; i < this.text.length; i++) {
            this.editor.delete();
        }
    }
}

class DeleteCommand implements Command {
    private editor: TextEditor;
    private deleted: string = '';

    constructor(editor: TextEditor) {
        this.editor = editor;
    }

    execute(): void {
        this.deleted = this.editor.getContent().slice(-1);
        this.editor.delete();
    }

    undo(): void {
        this.editor.append(this.deleted);
    }
}

// 创建一个调用者类,用于执行命令。
// 并创建历史记录用于管理撤销和重做操作。
class CommandInvoker {
    private history: Command[] = [];
    private undoneCommands: Command[] = [];

    executeCommand(command: Command): void {
        command.execute();
        this.history.push(command);
        this.undoneCommands = []; // 清空撤销历史
    }

    undo(): void {
        const command = this.history.pop();
        if (command) {
            command.undo();
            this.undoneCommands.push(command);
        }
    }

    redo(): void {
        const command = this.undoneCommands.pop();
        if (command) {
            command.execute();
            this.history.push(command);
        }
    }
}

const editor = new TextEditor();
const invoker = new CommandInvoker();

// 执行一些操作
invoker.executeCommand(new AppendCommand(editor, 'Hello'));
invoker.executeCommand(new AppendCommand(editor, ' World'));
console.log(editor.getContent()); // 输出: Hello World

// 撤销操作
invoker.undo();
console.log(editor.getContent()); // 输出: Hello

// 重做操作
invoker.redo();
console.log(editor.getContent()); // 输出: Hello World

处理UI交互

在图形用户界面应用中,命令模式通常被用于处理如按钮点击、菜单选择等用户交互动作。通过将这些动作封装成命令对象,可以更灵活地处理用户输入,使代码更易于管理和扩展。

// npm run code src/code/design-pattern/command/handling-ui-interactions.ts

export {};

// 命令接口
interface Command {
    execute(): void;
}

// 接收者类
class Receiver {
    performAction(action: string) {
        console.log(`Executing action: ${action}`);
    }
}

// 具体命令类
class ConcreteCommand implements Command {
    private receiver: Receiver;
    private action: string;

    constructor(receiver: Receiver, action: string) {
        this.receiver = receiver;
        this.action = action;
    }

    execute(): void {
        this.receiver.performAction(this.action);
    }
}

// 调用者类
class Invoker {
    private command!: Command;

    setCommand(command: Command) {
        this.command = command;
    }

    runCommand() {
        this.command.execute();
    }
}

// 客户端
class Client {
    private invoker: Invoker;
    private receiver: Receiver;

    constructor() {
        this.receiver = new Receiver();
        this.invoker = new Invoker();
    }

    run() {
        const command = new ConcreteCommand(this.receiver, "Save");
        this.invoker.setCommand(command);
        this.invoker.runCommand();
    }
}

// 使用示例
const client = new Client();
client.run();

异步任务和队列

命令模式可以用来管理异步任务和队列,非常适合用于处理多线程应用程序中的后台任务。可以通过封装任务为命令对象,并将它们放入一个队列中,然后在后台线程或异步操作中执行这些任务。

// npm run code src/code/design-pattern/command/asynchronous-queues.ts

export {};

// 命令接口
interface Command {
    execute(): Promise<void>;
}

// 具体命令类
class ConcreteCommand implements Command {
    private payload: string;

    constructor(payload: string) {
        this.payload = payload;
    }

    async execute(): Promise<void> {
        console.log(`Executing command with payload: ${this.payload}`);
        // 模拟异步操作
        await new Promise(resolve => setTimeout(resolve, 1000));
        console.log(`Command with payload ${this.payload} executed`);
    }
}

// 命令队列类
class CommandQueue {
    private queue: Command[] = [];

    addCommand(command: Command) {
        this.queue.push(command);
    }

    async run(): Promise<void> {
        while (this.queue.length > 0) {
            const command = this.queue.shift();
            if (command) {
                await command.execute();
            }
        }
    }
}

// 客户端
class Client {
    private commandQueue: CommandQueue;

    constructor() {
        this.commandQueue = new CommandQueue();
    }

    addTask(payload: string) {
        const command = new ConcreteCommand(payload);
        this.commandQueue.addCommand(command);
    }

    runTasks() {
        this.commandQueue.run();
    }
}

// 使用示例
const client = new Client();
client.addTask("Task 1");
client.addTask("Task 2");
client.addTask("Task 3");
client.runTasks();

日志和事务系统

命令模式可以用来记录每个操作的历史,适合用来实现日志和事务系统,并在需要时重放这些操作,对于实现撤销/重做功能、事务回滚功能特别有用。

// npm run code src/code/design-pattern/command/transaction-systems.ts

export {};

// 命令接口
interface Command {
    execute(): void;
    undo(): void;
}

// 具体命令类
class ConcreteCommand implements Command {
    private state: any;

    constructor(private receiver: any, private action: string) {
        this.state = receiver.state;
    }

    execute(): void {
        console.log(`Executing action: ${this.action}`);
        // 保存当前状态,然后执行操作
        this.state = this.receiver.state;
        this.receiver.performAction(this.action);
    }

    undo(): void {
        console.log(`Undoing action: ${this.action}`);
        // 撤销操作,恢复到之前的状态
        this.receiver.state = this.state;
    }
}

// 接收者类
class Receiver {
    state: any;

    performAction(action: string) {
        this.state = action;  // 举例修改状态
    }
}

// 命令管理类
class CommandManager {
    private history: Command[] = [];

    executeCommand(command: Command) {
        command.execute();
        this.history.push(command);
    }

    undo() {
        const command = this.history.pop();
        if (command) {
            command.undo();
        }
    }
}

// 客户端
class Client {
    private commandManager: CommandManager;
    private receiver: Receiver;

    constructor() {
        this.commandManager = new CommandManager();
        this.receiver = new Receiver();
    }

    run() {
        const command1 = new ConcreteCommand(this.receiver, "Action 1");
        const command2 = new ConcreteCommand(this.receiver, "Action 2");

        this.commandManager.executeCommand(command1);
        this.commandManager.executeCommand(command2);

        // 撤销最后一个操作
        this.commandManager.undo();
    }
}

// 使用示例
const client = new Client();
client.run();

远程过程调用(RPC)

命令模式可以用于封装远程过程调用,使远程调用的细节对客户端透明。在这个例子中,命令对象充当了客户端和远程服务之间的中间层,它封装了远程调用的所有细节。

// npm run code src/code/design-pattern/command/remote-procedure-call.ts

export {};

// 命令接口
interface Command {
    execute(): Promise<void>;
}

// 远程服务类
class RemoteService {
    async performAction(action: string): Promise<string> {
        // 模拟远程服务调用
        console.log(`Performing remote action: ${action}`);
        return new Promise(resolve => setTimeout(() => resolve(`Result of ${action}`), 1000));
    }
}

// 远程命令类
class RemoteCommand implements Command {
    constructor(private service: RemoteService, private action: string) {}

    async execute(): Promise<void> {
        // 调用远程服务
        const result = await this.service.performAction(this.action);
        console.log(`Received result: ${result}`);
    }
}

// 客户端
class Client {
    private command: Command;

    constructor(service: RemoteService, action: string) {
        this.command = new RemoteCommand(service, action);
    }

    run() {
        this.command.execute();
    }
}

// 使用示例
const remoteService = new RemoteService();
const client = new Client(remoteService, "Action 1");
client.run();

宏命令

宏命令允许将多个命令封装成一个单一的命令,然后一次性执行这些命令。这在需要录制和重放一系列操作的场景(比如宏录制、批处理任务等)中非常有用。

// npm run code src/code/design-pattern/command/macrocommand.ts

export {};

// 命令接口
interface Command {
    execute(): void;
}

// 具体命令类
class ConcreteCommand implements Command {
    private payload: string;

    constructor(payload: string) {
        this.payload = payload;
    }

    execute(): void {
        console.log(`Executing command: ${this.payload}`);
    }
}

// 宏命令类
class MacroCommand implements Command {
    private commands: Command[] = [];

    add(command: Command) {
        this.commands.push(command);
    }

    execute(): void {
        this.commands.forEach(command => command.execute());
    }
}

// 客户端
class Client {
    private macroCommand: MacroCommand;

    constructor() {
        this.macroCommand = new MacroCommand();
    }

    addCommand(command: Command) {
        this.macroCommand.add(command);
    }

    run() {
        this.macroCommand.execute();
    }
}

// 使用示例
const client = new Client();
client.addCommand(new ConcreteCommand("Action 1"));
client.addCommand(new ConcreteCommand("Action 2"));
client.addCommand(new ConcreteCommand("Action 3"));
client.run();

上次编辑于: