当前位置: 首页 > news >正文

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

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


一、命令模式核心思想

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


二、命令模式类图(Mermaid)

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

三、代码实现示例

1. 智能家居控制场景

// 接收者:灯光
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. 宏命令(批量操作)

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. 命令历史记录(支持多级撤销)

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

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

十、常见问题解答

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

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

Q2:如何处理命令参数?

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:命令模式如何支持异步?

class AsyncCommand implements Command {private Command command;public void execute() {new Thread(command::execute).start();}
}

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

http://www.xdnf.cn/news/697015.html

相关文章:

  • XJTU-SY轴承振动数据集的json自封装
  • 深度学习论文: FastVLM: Efficient Vision Encoding for Vision Language Models
  • Test-Time Zero-Shot Temporal Action Localization
  • 操作系统导论 第38章:廉价冗余磁盘阵列(RAID)
  • 【C/C++】delete nullptr;
  • android系统framework的几个新面试题目(涉及binder,input,SurfaceFlinger带答案)
  • Tomcat运行比较卡顿进行参数调优
  • 案例解读 | 某外资在华汽车系统企业综合运维平台建设实践
  • Java消息队列应用:Kafka、RabbitMQ选择与优化
  • java读取excel数据中字段是否为金额格式
  • vue或者前端适配makedown推荐开源依赖
  • dart常用语法详解/数组list/map数据/class类详解
  • golang 柯里化(Currying)
  • 720全景展示:VR全景的技术原理及应用
  • Python进阶【一】 :线程、进程与协程
  • Vite Vue3 配置 Composition API 自动导入与项目插件拆分
  • 输配电行业国产PLM转型方案:南通禛华电气的云PLM研发转型
  • rsync 如何通过参数加上端口号
  • 大观杂志大观杂志社大观编辑部2025年第4期目录
  • Java 并发编程通关秘籍:多线程基础 + 锁机制 + 工具类 + 性能优化
  • Appium+python自动化(七)- 认识Appium- 上
  • 【AI算法工程师面试指北】大模型微调中的灾难性遗忘该如何避免?
  • 多台电脑共用一个ip地址可以吗?会怎么样
  • Screen 连接远程服务器(Ubuntu)
  • docker中多个容器相互访问的端口问题
  • YOLOv8 模型部署到树莓派的完整指南
  • Golang | gRPC demo
  • C++23 <spanstream>:基于 std::span 的高效字符串流处理
  • 软件检测:确保品质关键步骤,企业该如何选择检测方式?
  • 王树森推荐系统公开课 排序05:排序模型的特征