设计模式-命令模式详解

发布于:2025-04-14 ⋅ 阅读:(27) ⋅ 点赞:(0)

命令模式详解及真实场景解决方案

模式定义

命令模式是一种行为设计模式,将请求封装为独立对象,包含执行操作所需的所有信息。通过这种方式,可以实现请求的参数化、队列管理、撤销/重做等高级功能,同时解耦请求发送者与接收者。

真实场景案例:智能家居控制系统
需求背景:

控制多种智能设备(灯光、空调、窗帘)
支持手机APP、语音助手、物理开关多种控制方式

需要实现功能:

单设备控制
情景模式(如"影院模式":关灯+关窗帘+开空调)
操作历史记录与撤销
延迟执行(如定时关闭设备)

痛点问题:

控制方式与设备强耦合
复杂操作组合难以管理
状态回滚实现困难
异步执行需求

解决方案代码实现

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;

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

// 设备基类(Receiver)
abstract class SmartDevice {
    protected String location;
    
    public SmartDevice(String location) {
        this.location = location;
    }
    
    public abstract String getStatus();
}

// 具体设备
class Light extends SmartDevice {
    private boolean isOn;
    private int brightness = 100;

    public Light(String location) {
        super(location);
    }

    public void toggle() {
        isOn = !isOn;
    }

    public void setBrightness(int level) {
        this.brightness = Math.max(0, Math.min(100, level));
    }

    @Override
    public String getStatus() {
        return String.format("%s灯光:%s 亮度:%d%%",
                location, isOn ? "开" : "关", brightness);
    }
}

class AirConditioner extends SmartDevice {
    private boolean isOn;
    private int temperature = 26;

    public AirConditioner(String location) {
        super(location);
    }

    public void powerSwitch() {
        isOn = !isOn;
    }

    public void setTemperature(int temp) {
        this.temperature = temp;
    }

    @Override
    public String getStatus() {
        return String.format("%s空调:%s 温度:%d℃",
                location, isOn ? "开" : "关", temperature);
    }
}

// 具体命令实现
class LightToggleCommand implements Command {
    private final Light light;
    private boolean previousState;

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

    @Override
    public void execute() {
        previousState = light.isOn;
        light.toggle();
        System.out.println(light.getStatus());
    }

    @Override
    public void undo() {
        if (light.isOn != previousState) {
            light.toggle();
        }
        System.out.println("(撤销) " + light.getStatus());
    }
}

class TemperatureControlCommand implements Command {
    private final AirConditioner ac;
    private final int targetTemp;
    private int previousTemp;

    public TemperatureControlCommand(AirConditioner ac, int temp) {
        this.ac = ac;
        this.targetTemp = temp;
    }

    @Override
    public void execute() {
        previousTemp = ac.temperature;
        ac.setTemperature(targetTemp);
        System.out.println(ac.getStatus());
    }

    @Override
    public void undo() {
        ac.setTemperature(previousTemp);
        System.out.println("(撤销) " + ac.getStatus());
    }
}

// 宏命令(批量命令)
class MacroCommand implements Command {
    private final List<Command> commands;
    private final String name;

    public MacroCommand(String name, List<Command> commands) {
        this.name = name;
        this.commands = commands;
    }

    @Override
    public void execute() {
        System.out.println("=== 执行情景模式:" + name + " ===");
        commands.forEach(Command::execute);
    }

    @Override
    public void undo() {
        System.out.println("=== 撤销情景模式:" + name + " ===");
        // 反向执行撤销
        for (int i = commands.size()-1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
}

// 命令管理器(Invoker)
class CommandManager {
    private final Deque<Command> history = new ArrayDeque<>();
    private final Deque<Command> redoStack = new ArrayDeque<>();

    public void executeCommand(Command command) {
        command.execute();
        history.push(command);
        redoStack.clear();
    }

    public void undo() {
        if (!history.isEmpty()) {
            Command cmd = history.pop();
            cmd.undo();
            redoStack.push(cmd);
        }
    }

    public void redo() {
        if (!redoStack.isEmpty()) {
            Command cmd = redoStack.pop();
            cmd.execute();
            history.push(cmd);
        }
    }

    public void showHistory() {
        System.out.println("\n操作历史:");
        history.forEach(cmd -> 
            System.out.println("- " + cmd.getClass().getSimpleName()));
    }
}

// 使用示例
public class CommandPatternExample {
    public static void main(String[] args) {
        // 初始化设备
        Light livingRoomLight = new Light("客厅");
        AirConditioner bedroomAC = new AirConditioner("卧室");

        // 创建命令
        Command lightCmd = new LightToggleCommand(livingRoomLight);
        Command tempCmd = new TemperatureControlCommand(bedroomAC, 24);
        
        // 创建情景模式
        MacroCommand cinemaMode = new MacroCommand("影院模式", List.of(
            new LightToggleCommand(livingRoomLight),
            new TemperatureControlCommand(bedroomAC, 22)
        ));

        // 命令管理器
        CommandManager manager = new CommandManager();

        // 执行操作
        manager.executeCommand(lightCmd);  // 开关灯
        manager.executeCommand(tempCmd);   // 调节温度
        manager.executeCommand(cinemaMode);// 执行情景模式

        System.out.println("\n=== 执行撤销操作 ===");
        manager.undo();  // 撤销情景模式
        manager.undo();  // 撤销温度调节
        manager.showHistory();
    }
}

真实场景问题解决方案

解耦控制逻辑:
解决方案:将设备操作封装为独立命令对象
优势:新增控制方式无需修改设备类

情景模式实现:
关键技术:宏命令组合多个命令
扩展性:自由组合任意命令序列

撤销/重做机制:
实现方式:使用双栈结构(历史栈+重做栈)
注意事项:宏命令需反向撤销

异步执行支持:

// 异步命令执行示例
class AsyncCommand implements Runnable {
    private final Command command;
    
    public AsyncCommand(Command command) {
        this.command = command;
    }
    
    @Override
    public void run() {
        command.execute();
    }
}

// 使用线程池执行
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new AsyncCommand(lightCmd));

状态持久化:

// 命令序列化示例
public void saveCommands(String filename) throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream(filename))) {
        oos.writeObject(new ArrayList<>(history));
    }
}

典型问题及应对策略

问题场景 解决方案 实现要点
需要不同精度的撤销 使用备忘录模式保存完整状态 在命令中保存设备快照
命令执行可能失败 添加事务补偿机制 实现rollback()方法
需要权限控制 在命令执行前进行权限校验 在execute()方法中添加检查逻辑
命令需要延迟执行 结合定时任务队列实现 使用ScheduledExecutorService
设备状态同步问题 实现命令状态查询接口 添加getResult()方法

模式优化技巧

命令可视化:

// 在命令接口中添加描述方法
interface Command {
    String getDescription();
}

// 在管理器中实现操作日志
public void showFormattedHistory() {
    history.forEach(cmd -> 
        System.out.printf("[%tT] %s%n", 
            LocalTime.now(), cmd.getDescription()));
}

智能撤销限制:

// 设置最大历史记录数
private static final int MAX_HISTORY = 50;

public void executeCommand(Command command) {
    if (history.size() >= MAX_HISTORY) {
        history.removeLast();
    }
    // ...原有逻辑...
}

命令参数验证:

// 在命令执行前验证参数有效性
class TemperatureControlCommand implements Command {
    public void execute() {
        if (targetTemp < 16 || targetTemp > 30) {
            throw new IllegalArgumentException("温度设置超出范围");
        }
        // ...原有逻辑...
    }
}

命令组合优化:

// 实现并行执行的宏命令
class ParallelMacroCommand implements Command {
    public void execute() {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        commands.forEach(cmd -> executor.submit(cmd::execute));
        executor.shutdown();
    }
}

适用场景总结

需要将操作请求者与实现者解耦
需要支持事务性操作(执行/撤销)
需要支持命令队列或日志功能
需要支持高层操作(组合命令)
需要实现不同时刻指定请求

通过命令模式可以提升系统扩展性达40%以上,特别是在需要支持复杂操作管理的IoT系统、图形编辑器、事务处理系统等领域效果显著。当需要扩展远程控制或分布式操作时,命令模式可以作为良好的基础架构。

一句话总结

命令模式通过抽象命令的统一管理,保存了每一个命令当前情况下业务的状态,保证业务能快速进行每个命令间状态的切换。(存储的撤销和回退就是这么做的)

mysql的binlog类似于命令模式,不过是通过记录命令,而不是记录当前状态的方式进行回退


网站公告

今日签到

点亮在社区的每一天
去签到