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();
    }
}

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

相关推荐
都叫我大帅哥27 分钟前
Spring @Transactional 注解完全指南
java·spring
Java成神之路-34 分钟前
G1 垃圾回收器 :SATB + TAMS核心机制深度解析
java·jvm
前端工作日常1 小时前
我学习到的JIT即时编译与机器码缓存失效
java·后端
带刺的坐椅1 小时前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·solon·jetty·undertow·mcp-server·htttp
xcLeigh2 小时前
Go入门:变量声明的五种方式详解
java·开发语言·golang
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
Mark_ZP3 小时前
【锁1】Synchronized vs ReentrantLock区别
java
啦啦啦啦啦zzzz3 小时前
工具:动态类工厂和用配置文件存储属性
c++·设计模式·工具·动态工厂
立心者03 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端
枕星而眠3 小时前
C++ STL Map容器完全指南:从有序红黑树到无序哈希表
java·开发语言