命令设计模式是一种行为模式,可将请求转换为独立对象,允许您参数化作、将其排队、记录作或支持可撤消作,同时将发送方与接收方解耦。

它在以下情况下特别有用:
您希望将作封装为对象。
您需要对请求进行排队、延迟或记录。
你希望支持撤消/重做功能。
您希望将调用作的对象与知道如何执行作的对象分离。

让我们通过一个真实世界的示例来了解如何应用命令模式将调用器与执行器分离,并构建一个更灵活、可扩展和可测试的命令执行框架。

问题:紧密耦合的智能家居控制器
想象一下,您正在构建一个“智能家居中心”应用程序。

该集线器需要控制各种设备:
💡 智能灯
🌡️ 温控 器
🔒 安全系统
🔊 智能音箱
🚪 车库门

集线器应该能够发送命令,例如:
light.on(),light.off()
thermostat.setTemperature(22)
speaker.playMusic()

朴素的实现:一个控制器统治一切
你可以从一个简单的类开始,如下所示:

public class Light {
    public void on() {
        System.out.println("Light turned ON");
    }

    public void off() {
        System.out.println("Light turned OFF");
    }
}

恒温器

public class Thermostat {
    private int currentTemperature = 20; // default

    public void setTemperature(int temp) {
        System.out.println("Thermostat set to " + temp + "°C");
        currentTemperature = temp;
    }

    public int getCurrentTemperature() {
        return currentTemperature;
    }
}

控制器与设备紧密耦合

public class SmartHomeControllerV1 {
    private final Light light;
    private final Thermostat thermostat;

    public SmartHomeControllerV1(Light light, Thermostat thermostat) {
        this.light = light;
        this.thermostat = thermostat;
    }

    public void turnOnLight() {
        light.on();
    }

    public void turnOffLight() {
        light.off();
    }

    public void setThermostatTemperature(int temperature) {
        thermostat.setTemperature(temperature);
    }
}

用法示例

public class SmartHomeApp {
    public static void main(String[] args) {
        Light light = new Light();
        Thermostat thermostat = new Thermostat();
        SmartHomeControllerV1 controller = new SmartHomeControllerV1(light, thermostat);

        controller.turnOnLight();
        controller.setThermostatTemperature(22);
        controller.turnOffLight();
    }
}

为什么这种设计会随着系统的发展而失败
这个简单的控制器目前有效,但随着复杂性的增加,很快就会崩溃。

  1. 1. 紧密耦合
    该 类直接绑定到每个设备及其特定方法名称。SmartHomeControllerV1
    您无法重用或泛化作 — 每个新设备都需要控制器中的新方法。
  2. 2. 可扩展性差
    添加新设备(例如,,,)意味着:SprinklerGarageDoorSmartSpeaker
    向控制器添加新字段
    为每个作编写更多方法
    增加一个巨型职业的规模和复杂性
    很快,您的控制器就会变成一个臃肿的巨石。
  3. 3. 不支持撤消/重做
    无法撤消命令。
    想要实现“撤消上一个作”功能吗?您需要:
    手动跟踪设备状态
    为每个方法编写自定义撤消逻辑
    在某个地方添加一个大的 switch/if-else 块,以找出要撤消的作
    它脆弱、重复且容易出错。
  4. 4. 无需排期或排队
    假设用户设置了一个规则,如下所示:
    “晚上 7:00 开灯”
    您无法排队执行哪些作,因为作被硬编码到方法调用中,而不是表示为独立对象。
  5. 5. 无通用日志记录
    想要记录所采取的每项作?
    或者存储作历史记录以供以后重播?
    如果不在每个方法中复制日志记录代码,则不可能。
  6. 6. 困难的 UI 映射
    假设您正在构建一个带有可编程按钮的遥控器。
    或者具有可自定义场景的移动应用程序,例如“电影之夜”或“离开家”。
    每个 UI 按钮都需要自定义逻辑来知道要调用哪个设备以及如何调用——紧密耦合、难以扩展且无法重用。

我们真正需要什么
我们需要将每个命令(例如,“打开灯”、“将恒温器设置为 22°C”)视为一个独立的对象——封装:

该怎么办
它影响哪个设备
如何执行
(可选)如何撤消它
这样,控制器、远程或调度程序就不关心命令的工作原理——它只知道要执行哪个命令。
这正是命令设计模式所实现的。

命令模式
命令设计模式是一种行为模式,可将请求转换为独立对象,允许您:
参数化作
队列或日志作
支持撤消/重做
将作的调用方与执行作的接收方分离

您无需直接在设备类上调用方法,而是将每个请求(如 或 )封装为 Command 对象。light.on()thermostat.setTemperature(22)

类图

 

  1. 1. 命令(接口)
    定义用于执行作的标准接口,通常是一个 方法。execute()
    如果需要撤消功能,也可以声明一个方法。undo()
    作为所有具体命令将实现的抽象。
  2. 2. 具体命令
    实现 接口。Command
    维护对接收方的引用,并通过 委托给接收方的方法来实现。execute()
    还可以捕获支持的必要状态。undo()
  3. 3. 接收器
    执行实际工作的对象。
    它包含将由命令触发的业务逻辑。
    接收方不知道命令或调用者——它只是公开了作。
  4. 4. 调用者
    负责发起命令执行。
    它保存对对象的引用 ,并在 发生作(例如,按钮按下)时调用。Commandexecute()
    调用者不知道命令或接收者的具体情况——它只与命令接互。
  5. 5. 客户
    创建 ConcreteCommand 的实例,将每个实例与相应的 Receiver 相关联。
    使用要执行的命令配置 调用程序。
    客户端决定要执行哪些作并设置所有内容,但不执行命令本身。

这个怎么运作
每个命令都实现了一个标准接口,例如 (和可选的 execute()undo())
调用器(例如,远程控制、调度程序、UI)只需调用 command.execute()
接收方(例如, ,)在执行命令时执行实际作LightThermostat
这种分离允许高度灵活、解耦和可扩展的设计。

实现命令模式
让我们重构我们的智能家居控制器,以使用 支持可撤消作的命令模式。我们将每个作封装为命令,将调用者与逻辑分离,并允许撤消以前的作。

1:定义 接口Command
所有命令都必须实现 和 。execute()undo()

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

2:定义接收器(设备)
这些是执行作的实际智能家居设备。

💡Light

public class Light {
    public void on() {
        System.out.println("Light turned ON");
    }

    public void off() {
        System.out.println("Light turned OFF");
    }
}

🌡️Thermostat

public class Thermostat {
    private int currentTemperature = 20; // default

    public void setTemperature(int temp) {
        System.out.println("Thermostat set to " + temp + "°C");
        currentTemperature = temp;
    }

    public int getCurrentTemperature() {
        return currentTemperature;
    }
}
  1. 3. 实现具体命令类
    每个命令都封装了一个特定的作,并且知道如何撤消它。

LightOn命令

public class LightOnCommand implements Command {
    private final Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}

LightOff命令

public class LightOffCommand implements Command {
    private final Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }

    @Override
    public void undo() {
        light.on();
    }
}

SetTemperatureCommand

public class SetTemperatureCommand implements Command {
    private final Thermostat thermostat;
    private final int newTemperature;
    private int previousTemperature;

    public SetTemperatureCommand(Thermostat thermostat, int temperature) {
        this.thermostat = thermostat;
        this.newTemperature = temperature;
    }

    @Override
    public void execute() {
        previousTemperature = thermostat.getCurrentTemperature();
        thermostat.setTemperature(newTemperature);
    }

    @Override
    public void undo() {
        thermostat.setTemperature(previousTemperature);
    }
}
  1. 4. 创建支持撤消的调用器()SmartButton
import java.util.Stack;

public class SmartButton {
    private Command currentCommand;
    private final Stack<Command> history = new Stack<>();

    public void setCommand(Command command) {
        this.currentCommand = command;
    }

    public void press() {
        if (currentCommand != null) {
            currentCommand.execute();
            history.push(currentCommand);
        } else {
            System.out.println("No command assigned.");
        }
    }

    public void undoLast() {
        if (!history.isEmpty()) {
            Command lastCommand = history.pop();
            lastCommand.undo();
        } else {
            System.out.println("Nothing to undo.");
        }
    }
}
  1. 5. 客户端代码 – 使用命令系统
public class CommandPatternDemo {
    public static void main(String[] args) {
        // Receivers
        Light light = new Light();
        Thermostat thermostat = new Thermostat();

        // Commands
        Command lightOn = new LightOnCommand(light);
        Command lightOff = new LightOffCommand(light);
        Command setTemp22 = new SetTemperatureCommand(thermostat, 22);

        // Invoker
        SmartButton button = new SmartButton();

        // Simulate usage
        System.out.println("→ Pressing Light ON");
        button.setCommand(lightOn);
        button.press();

        System.out.println("→ Pressing Set Temp to 22°C");
        button.setCommand(setTemp22);
        button.press();

        System.out.println("→ Pressing Light OFF");
        button.setCommand(lightOff);
        button.press();

        System.out.println("\n🔁 Undo Last Action");
        button.undoLast(); // Undo Light OFF

        System.out.println("🔁 Undo Previous Action");
        button.undoLast(); // Undo Set Temp

        System.out.println("🔁 Undo Again");
        button.undoLast(); // Undo Light ON

        System.out.println("🔁 Undo Once More");
        button.undoLast(); // No more actions
    }
}

输出

→ Pressing Light ON
Light turned ON
→ Pressing Set Temp to 22°C
Thermostat set to 22°C
→ Pressing Light OFF
Light turned OFF

🔁 Undo Last Action
Light turned ON
🔁 Undo Previous Action
Thermostat set to 20°C
🔁 Undo Again
Light turned OFF

我们取得了什么成就
封装命令:每个作都是一个可重用、可撤消的对象
解耦的 UI/逻辑:调用者不知道命令是如何工作的
撤消支持:每个命令跟踪并恢复自己的效果
可扩展性:轻松添加、等。PlayMusicCommandOpenGarageCommand
历史记录跟踪:命令历史记录支持撤消/重做或日志记录

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐