Java设计模式之命令模式详解

Java设计模式之命令模式详解


一、命令模式核心思想

核心目标将请求封装为对象,使请求的发送者与接收者解耦。支持请求的排队、记录、撤销等操作,如同餐厅点餐系统:顾客(发送者)→ 订单(命令对象)→ 厨师(接收者)。


二、命令模式类图(Mermaid)

持有 调用 创建 创建 配置 Invoker -command: Command +setCommand(Command) +executeCommand() <<interface>> Command +execute() +undo() ConcreteCommand -receiver: Receiver -state +execute() +undo() Receiver +action() Client


三、代码实现示例

1. 智能家居控制场景

java 复制代码
// 接收者:灯光
class Light {
    private boolean isOn = false;
    
    public void turnOn() {
        isOn = true;
        System.out.println("灯光已打开");
    }
    
    public void turnOff() {
        isOn = false;
        System.out.println("灯光已关闭");
    }
    
    public boolean isOn() { return isOn; }
}

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

// 具体命令:开灯
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
    
    public void undo() {
        light.turnOff();
    }
}

// 调用者:遥控器按钮
class RemoteControlButton {
    private Command command;
    private Command lastCommand;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
        lastCommand = command;
    }
    
    public void pressUndo() {
        if (lastCommand != null) {
            lastCommand.undo();
            lastCommand = null;
        }
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        Light livingRoomLight = new Light();
        Command lightOn = new LightOnCommand(livingRoomLight);
        
        RemoteControlButton button = new RemoteControlButton();
        button.setCommand(lightOn);
        
        button.pressButton();   // 输出:灯光已打开
        button.pressUndo();     // 输出:灯光已关闭
    }
}

四、模式优缺点分析

✅ 优势

  • 解耦请求者与执行者:发送者无需知道接收者细节
  • 支持高级操作:轻松实现撤销/重做、事务、队列等功能
  • 灵活扩展:新增命令无需修改已有代码
  • 组合命令:支持宏命令(批量执行)

❌ 缺点

  • 类数量增加:每个命令都需要单独类
  • 过度设计风险:简单操作可能不适用

五、典型应用场景

  1. GUI操作:菜单项点击事件处理
  2. 事务系统:数据库操作回滚
  3. 任务队列:线程池任务调度
  4. 宏命令:批量执行操作(如IDE快捷键)
  5. 游戏控制:角色动作的撤销/重做

六、Mermaid序列图(命令执行流程)

Client Invoker Command Receiver setCommand(Command) executeCommand() execute() action() Client Invoker Command Receiver


七、命令模式 vs 其他模式

对比模式 核心区别
策略模式 封装算法,行为可替换
职责链模式 请求沿链传递直到被处理
备忘录模式 保存对象状态用于恢复

八、高级应用技巧

1. 宏命令(批量操作)

java 复制代码
class MacroCommand implements Command {
    private List<Command> commands = new ArrayList<>();
    
    public void add(Command cmd) {
        commands.add(cmd);
    }
    
    public void execute() {
        commands.forEach(Command::execute);
    }
    
    public void undo() {
        // 逆序执行撤销
        for (int i = commands.size()-1; i >=0; i--) {
            commands.get(i).undo();
        }
    }
}

// 使用示例
MacroCommand partyMode = new MacroCommand();
partyMode.add(new LightOnCommand(light));
partyMode.add(new MusicPlayCommand(speaker));
partyMode.execute();  // 同时执行开灯和播放音乐

2. 命令历史记录(支持多级撤销)

java 复制代码
class CommandHistory {
    private Stack<Command> history = new Stack<>();
    
    public void push(Command cmd) {
        history.push(cmd);
    }
    
    public Command pop() {
        return history.pop();
    }
    
    public void undoAll() {
        while (!history.isEmpty()) {
            history.pop().undo();
        }
    }
}

九、实际框架应用案例

1. Java Swing的Action接口

<<interface>> Action +actionPerformed(ActionEvent) AbstractAction +actionPerformed(ActionEvent) JButton +setAction(Action)

2. Spring的JdbcTemplate

java 复制代码
// 命令模式变体:模板方法+回调命令
jdbcTemplate.execute(new ConnectionCallback<Object>() {
    public Object doInConnection(Connection conn) {
        // 执行SQL命令
        return null;
    }
});

十、常见问题解答

Q1:如何防止命令对象膨胀?

  • 使用Lambda表达式(Java 8+)
java 复制代码
button.setCommand(() -> light.turnOn());

Q2:如何处理命令参数?

java 复制代码
class DimLightCommand implements Command {
    private Light light;
    private int prevBrightness;
    private int newBrightness;
    
    public DimLightCommand(Light light, int brightness) {
        this.light = light;
        this.newBrightness = brightness;
    }
    
    public void execute() {
        prevBrightness = light.getBrightness();
        light.setBrightness(newBrightness);
    }
    
    public void undo() {
        light.setBrightness(prevBrightness);
    }
}

Q3:命令模式如何支持异步?

java 复制代码
class AsyncCommand implements Command {
    private Command command;
    
    public void execute() {
        new Thread(command::execute).start();
    }
}

如果文章对您有帮助,请帮忙点关注支持一下吧,谢谢啦!

相关推荐
guestsun5 分钟前
SpringBoot七大事务失效场景分析
java·spring boot·mybatis
毕设源码-邱学长6 小时前
【开题答辩全过程】以 基于Java的学校住宿管理系统的设计与实现为例,包含答辩的问题和答案
java·开发语言
兑生7 小时前
【灵神题单·贪心】1481. 不同整数的最少数目 | 频率排序贪心 | Java
java·开发语言
daidaidaiyu7 小时前
一文学习 Spring 声明式事务源码全流程总结
java·spring
零雲9 小时前
java面试:了解抽象类与接口么?讲一讲它们的区别
java·开发语言·面试
左左右右左右摇晃12 小时前
Java并发——synchronized锁
java·开发语言
sxlishaobin12 小时前
Java I/O 模型详解:BIO、NIO、AIO
java·开发语言·nio
彭于晏Yan12 小时前
Spring AI(二):入门使用
java·spring boot·spring·ai
有一个好名字13 小时前
vibe codeing 开发流程
java
兑生13 小时前
【灵神题单·贪心】3745. 三元素表达式的最大值 | 排序贪心 | Java
java·开发语言