JavaScript设计模式「基于ES2024」:行为型模式-命令模式

71 阅读2分钟

命令模式是一种行为设计模式,它将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

// 命令接口
class Command {
    execute() {
        throw new Error('Method not implemented');
    }

    undo() {
        throw new Error('Method not implemented');
    }
}

// 接收者类:灯
class Light {
    #isOn = false;
    #brightness = 0;

    turnOn() {
        this.#isOn = true;
        console.log("Light is turned on");
    }

    turnOff() {
        this.#isOn = false;
        console.log("Light is turned off");
    }

    setBrightness(level) {
        this.#brightness = level;
        console.log(`Light brightness set to ${level}%`);
    }

    get status() {
        return `Light is ${this.#isOn ? 'on' : 'off'}, brightness: ${this.#brightness}%`;
    }
}

// 具体命令:开灯
class LightOnCommand extends Command {
    #light;

    constructor(light) {
        super();
        this.#light = light;
    }

    execute() {
        this.#light.turnOn();
    }

    undo() {
        this.#light.turnOff();
    }
}

// 具体命令:关灯
class LightOffCommand extends Command {
    #light;

    constructor(light) {
        super();
        this.#light = light;
    }

    execute() {
        this.#light.turnOff();
    }

    undo() {
        this.#light.turnOn();
    }
}

// 具体命令:调节亮度
class DimLightCommand extends Command {
    #light;
    #prevBrightness;
    #targetBrightness;

    constructor(light, brightness) {
        super();
        this.#light = light;
        this.#targetBrightness = brightness;
    }

    execute() {
        this.#prevBrightness = this.#light.status.match(/brightness: (\d+)%/)[1];
        this.#light.setBrightness(this.#targetBrightness);
    }

    undo() {
        this.#light.setBrightness(this.#prevBrightness);
    }
}

// 调用者:遥控器
class RemoteControl {
    #commands = new Map();
    #history = [];

    setCommand(buttonName, command) {
        this.#commands.set(buttonName, command);
    }

    pressButton(buttonName) {
        const command = this.#commands.get(buttonName);
        if (command) {
            command.execute();
            this.#history.push(command);
        } else {
            console.log(`Button ${buttonName} is not programmed`);
        }
    }

    undoLastCommand() {
        const command = this.#history.pop();
        if (command) {
            command.undo();
        } else {
            console.log("No commands to undo");
        }
    }
}

// 客户端代码
function clientCode() {
    const livingRoomLight = new Light();
    const remoteControl = new RemoteControl();

    remoteControl.setCommand("on", new LightOnCommand(livingRoomLight));
    remoteControl.setCommand("off", new LightOffCommand(livingRoomLight));
    remoteControl.setCommand("dim", new DimLightCommand(livingRoomLight, 50));

    console.log("Pressing 'on' button:");
    remoteControl.pressButton("on");
    console.log(livingRoomLight.status);

    console.log("\nPressing 'dim' button:");
    remoteControl.pressButton("dim");
    console.log(livingRoomLight.status);

    console.log("\nPressing 'off' button:");
    remoteControl.pressButton("off");
    console.log(livingRoomLight.status);

    console.log("\nUndoing last command:");
    remoteControl.undoLastCommand();
    console.log(livingRoomLight.status);

    console.log("\nUndoing another command:");
    remoteControl.undoLastCommand();
    console.log(livingRoomLight.status);
}

clientCode();

实现思路

  1. Command 接口:定义了所有具体命令必须实现的 executeundo 方法。

  2. Light 类(接收者)

    • 实现了灯的基本功能,如开关和调节亮度。
    • 使用私有字段 #isOn#brightness 来存储灯的状态。
  3. 具体命令类(LightOnCommand, LightOffCommand, DimLightCommand

    • 每个类封装了一个特定的操作。
    • 持有对接收者(Light)的引用。
    • 实现了 executeundo 方法。
  4. RemoteControl 类(调用者)

    • 使用 Map 来存储按钮和对应的命令。
    • 维护一个命令历史,用于实现撤销功能。
    • 提供了设置命令、执行命令和撤销命令的方法。

优点

  • 解耦:将请求的发送者和接收者解耦。
  • 扩展性:可以轻松添加新的命令,而不需要修改现有代码。
  • 组合:可以将简单命令组合成复杂的命令。
  • 撤销/重做:通过维护历史记录,可以实现撤销和重做功能。
  • 延迟执行:命令可以被存储和调度,以便稍后执行。