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

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

相关推荐
superkcl20223 小时前
【JAVA】【Stream流】
java·windows·python
mldong3 小时前
mldong 快速开发框架登录模块设计与实现
java·后端·架构
bulucc3 小时前
Maven 或 Gradle 下载和添加 jar 文件的步骤
java·maven·jar
我爱Jack3 小时前
@annotation:Spring AOP 的“精准定位器“
java·后端·spring
一ge科研小菜鸡4 小时前
编程语言的演化与选择:技术浪潮中的理性决策
java·c语言·python
我崽不熬夜4 小时前
为什么你该立即学习 Java 的 Lambda 表达式?
java·后端·java ee
wsdchong之小马过河4 小时前
2025虚幻引擎文件与文件夹命名规律
java·数据库·虚幻
幸运的大号暖贴4 小时前
单点登录进阶:基于芋道(yudao)授权码模式的单点登录流程、代码实现与安全设计
java·安全
小白的代码日记4 小时前
IDEA 中 Tomcat 部署 Java Web 项目(Maven 多模块 & 非 Maven 通用版)(linux+windows)
java·tomcat·intellij-idea
coder_zh_5 小时前
Spring Boot自动配置原理
java·spring boot·spring