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

Java 23 种设计模式:从踩坑到精通 | 番外:命令模式 —— 仓储设备控制实战

Java 23 种设计模式:从踩坑到精通 | 番外:命令模式 —— 仓储设备控制实战

摘要:命令模式将“请求”封装为对象,从而让你可以用不同的请求对客户端参数化、对请求排队或记录日志,并天然支持撤销操作。本文结合智能仓储设备控制系统的场景,完整展示如何用命令模式实现对传送带、分拣机等设备的远程控制和撤销操作,并与策略模式深度对比,帮你掌握“将行为对象化”的设计精髓。

🗺️本文阅读地图(3 分钟速览)

  • 为什么设备控制不能直接调用设备方法?
  • ✅ 命令模式核心角色:调用者、抽象命令、具体命令、接收者
  • 手写仓储调度中心:启动/停止设备,支持多步撤销
  • 🚀 命令 vs 策略:行为封装 vs 算法替换
  • 🎤 面试必问:“命令模式和策略模式有什么区别?Runnable用了哪个?”

📖《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 正篇:命令模式 Command —— 把操作封装成对象,实现撤销与排队 |当前:番外 · 命令模式 × 仓储设备控制
🔗 返回系列总目录

1. 仓储设备控制的痛点

在智能仓库中,有传送带、分拣机、堆垛机等多种设备。操作员通过一个中央控制台来管理这些设备的启停。如果直接在控制台类中调用设备方法:

if(button.equals("start_conveyor")){conveyor1.start();}elseif(button.equals("stop_sorter")){sorter1.stop();}

这种写法将控制台与具体设备紧耦合,新增设备或修改操作逻辑都需要修改控制台代码。更复杂的是,操作员希望控制台支持撤销上一步操作一键批量操作——这用if-else几乎无法优雅实现。

命令模式的解决思路:将“启动设备”和“停止设备”这些操作封装为独立的命令对象,控制台只持有命令并触发执行,不关心命令内部是哪个设备、如何执行。所有执行过的命令记录在栈中,撤销时只需弹出栈顶并调用undo()

1.1 你的场景该不该用命令模式?

判断标准是 → 用命令模式否 → 用其他方式
需要将操作的发起者与执行者解耦
需要支持撤销/重做、命令队列、宏命令
需要在运行时动态指定、排列或执行请求
只有简单的调用,无需撤销或排队直接调用即可

2. 命令模式 UML(仓储设备控制场景)


3. 完整源码实现

3.1 抽象命令接口 (Command)

/** * 抽象命令:声明执行和撤销操作 */publicinterfaceCommand{voidexecute();voidundo();}

💬白话:所有命令都必须能“执行”和“撤销”。这就是命令模式的灵魂——命令不仅是“做什么”,还要知道“怎么反悔”。

3.2 接收者:仓库设备基类 (WarehouseDevice)

/** * 接收者:仓库设备基类 */publicabstractclassWarehouseDevice{protectedStringname;protectedbooleanisRunning;publicWarehouseDevice(Stringname){this.name=name;this.isRunning=false;}publicabstractvoidstart();publicabstractvoidstop();publicStringgetName(){returnname;}publicbooleanisRunning(){returnisRunning;}}

3.3 具体接收者:传送带 (ConveyorBelt)

/** * 具体接收者:传送带设备 */publicclassConveyorBeltextendsWarehouseDevice{publicConveyorBelt(Stringname){super(name);}@Overridepublicvoidstart(){isRunning=true;System.out.println("🏭 "+name+" 启动:传送带开始运转,速度 1.5m/s");}@Overridepublicvoidstop(){isRunning=false;System.out.println("🏭 "+name+" 停止:传送带已关闭");}}

3.4 具体接收者:分拣机 (SortingMachine)

/** * 具体接收者:分拣机设备 */publicclassSortingMachineextendsWarehouseDevice{publicSortingMachine(Stringname){super(name);}@Overridepublicvoidstart(){isRunning=true;System.out.println("🏭 "+name+" 启动:分拣机开始工作,处理能力 500件/分钟");}@Overridepublicvoidstop(){isRunning=false;System.out.println("🏭 "+name+" 停止:分拣机已关闭");}}

💬白话:这些设备就是“真正干活的”——传送带知道怎么启动和停止自己,分拣机也知道。它们不关心是谁发出的指令。

3.5 具体命令:启动/停止设备命令

/** * 具体命令:启动设备 */publicclassStartDeviceCommandimplementsCommand{privateWarehouseDevicedevice;publicStartDeviceCommand(WarehouseDevicedevice){this.device=device;}@Overridepublicvoidexecute(){device.start();}@Overridepublicvoidundo(){device.stop();}}/** * 具体命令:停止设备 */publicclassStopDeviceCommandimplementsCommand{privateWarehouseDevicedevice;publicStopDeviceCommand(WarehouseDevicedevice){this.device=device;}@Overridepublicvoidexecute(){device.stop();}@Overridepublicvoidundo(){device.start();}}

💬白话:启动命令的撤销就是停止,停止命令的撤销就是重新启动——这是最简洁的撤销实现。

3.6 调用者:设备调度中心 (DeviceScheduler)

importjava.util.Stack;/** * 调用者:设备调度中心(遥控器) */publicclassDeviceScheduler{privateStack<Command>commandHistory=newStack<>();publicvoidexecuteCommand(Commandcommand){command.execute();commandHistory.push(command);System.out.println(" ✅ 命令已执行,当前历史记录:"+commandHistory.size()+" 条\n");}publicvoidundoLastCommand(){if(commandHistory.isEmpty()){System.out.println(" ⚠️ 无命令可撤销\n");return;}Commandcommand=commandHistory.pop();command.undo();System.out.println(" ↩️ 已撤销上一个命令,剩余历史记录:"+commandHistory.size()+" 条\n");}}

💬白话:调度中心就像遥控器——它只管按下“执行”或“撤销”按钮,完全不知道控制的是传送带还是分拣机。所有执行过的命令压入栈中,撤销时弹出栈顶。

3.7 客户端测试

publicclassClient{publicstaticvoidmain(String[]args){System.out.println("=== 智能仓储设备控制系统(命令模式)===\n");ConveyorBeltconveyor1=newConveyorBelt("传送带A-1");ConveyorBeltconveyor2=newConveyorBelt("传送带B-2");SortingMachinesorter1=newSortingMachine("分拣机S-1");DeviceSchedulerscheduler=newDeviceScheduler();// 场景1~3:正常执行scheduler.executeCommand(newStartDeviceCommand(conveyor1));scheduler.executeCommand(newStartDeviceCommand(sorter1));scheduler.executeCommand(newStopDeviceCommand(conveyor1));// 场景4:撤销停止 → 传送带恢复运行scheduler.undoLastCommand();// 场景5:批量启动scheduler.executeCommand(newStartDeviceCommand(conveyor2));scheduler.executeCommand(newStartDeviceCommand(sorter1));// 场景6:紧急连续撤销scheduler.undoLastCommand();scheduler.undoLastCommand();}}

4. 运行结果

=== 智能仓储设备控制系统(命令模式)=== 🏭 传送带A-1 启动:传送带开始运转,速度 1.5m/s ✅ 命令已执行,当前历史记录:1 条 🏭 分拣机S-1 启动:分拣机开始工作,处理能力 500件/分钟 ✅ 命令已执行,当前历史记录:2 条 🏭 传送带A-1 停止:传送带已关闭 ✅ 命令已执行,当前历史记录:3 条 🏭 传送带A-1 启动:传送带开始运转,速度 1.5m/s ↩️ 已撤销上一个命令,剩余历史记录:2 条 🏭 传送带B-2 启动:传送带开始运转,速度 1.5m/s ✅ 命令已执行,当前历史记录:3 条 🏭 分拣机S-1 启动:分拣机开始工作,处理能力 500件/分钟 ✅ 命令已执行,当前历史记录:4 条 🏭 分拣机S-1 停止:分拣机已关闭 ↩️ 已撤销上一个命令,剩余历史记录:3 条 🏭 传送带B-2 停止:传送带已关闭 ↩️ 已撤销上一个命令,剩余历史记录:2 条

5. 核心角色回顾

角色职责对应代码
Command定义执行和撤销的接口Command
ConcreteCommand绑定接收者与动作StartDeviceCommand/StopDeviceCommand
Receiver真正执行具体业务逻辑ConveyorBelt/SortingMachine
Invoker持有命令,触发执行与撤销DeviceScheduler

6. 命令模式 vs 策略模式

对比项命令模式策略模式
意图封装请求为对象,支持撤销/队列封装算法族,使其可相互替换
是否支持撤销✅ 核心特性❌ 通常不需要
典型应用Runnable、仓储设备控制、事务回滚Comparator、支付方式、促销策略

💡一句话记忆:命令模式是“行为对象化”——把操作变成对象,能执行也能撤销;策略模式是“算法对象化”——把算法变成对象,能灵活替换。

7. 命令模式的优缺点

优点缺点
调用者与接收者完全解耦每个命令一个类,命令多时类数量膨胀
天然支持撤销/重做、宏命令设计复杂度增加,角色较多
符合开闭原则:新增命令无需修改现有代码命令对象持有状态时需注意线程安全

8. 六大设计原则体现

原则体现
单一职责命令只负责请求封装,设备只负责执行
开闭原则新增命令无需修改调度中心或设备
里氏替换所有命令可替换Command接口
依赖倒置调度中心依赖抽象Command
接口隔离Command只有execute()undo()
迪米特法则调度中心只与命令交互,不知设备细节

附 命令模式 UML源码(仓储设备控制场景)

@startuml title Java 23 种设计模式:从踩坑到精通 footer 折哥 | 智能物流与Java实战 ' 1. 全局样式配置 skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam classBorderColor #333333 skinparam classFontColor #1A1A1A skinparam classFontSize 14 skinparam noteFontSize 12 skinparam noteFontColor #555555 skinparam arrowColor #555555 skinparam classBackgroundColor #F9F9F9 skinparam interface { BackgroundColor #E8F5E9 BorderColor #2E7D32 } ' 2. 调用者(遥控器/调度中心) class Invoker { - command : Command + setCommand(Command) + executeCommand() + undoLastCommand() } note right of Invoker <b>调用者(调度中心)</b> -- 持有命令对象,触发执行 不关心具体执行细节 支持撤销操作 end note ' 3. 抽象命令 interface Command { + execute() + undo() } note right of Command <b>抽象命令</b> -- 声明执行和撤销操作 所有具体命令都实现此接口 end note ' 4. 具体命令:开启 class LightOnCommand implements Command { - light : Light + execute() + undo() } note right of LightOnCommand <b>具体命令:开启</b> -- 持有接收者引用 执行时调用接收者的on() 撤销时调用接收者的off() end note ' 5. 具体命令:关闭 class LightOffCommand implements Command { - light : Light + execute() + undo() } note right of LightOffCommand <b>具体命令:关闭</b> -- 持有接收者引用 执行时调用接收者的off() 撤销时调用接收者的on() end note ' 6. 接收者 class Light { + on() + off() } note right of Light <b>接收者(执行者)</b> -- 真正执行具体业务逻辑 与命令解耦,无需知道谁调用 end note ' 7. 关系连线 Invoker o--> Command : 持有命令 LightOnCommand --> Light : 调用 LightOffCommand --> Light : 调用 LightOnCommand ..|> Command : 实现 LightOffCommand ..|> Command : 实现 @enduml

🧭 《Java 23 种设计模式:从踩坑到精通》快速导航

  • 开篇:系列介绍与目录
  • 正篇:命令模式 Command —— 把操作封装成对象,实现撤销与排队
  • 当前:番外 · 命令模式 × 仓储设备控制(你在这里)
  • 创建型模式汇总
  • 结构型模式汇总
  • 行为型模式汇总

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。

📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》、《电商多平台电子面单对接实战》。技术相通,思路可鉴。

http://www.jsqmd.com/news/1317010/

相关文章:

  • 定点数与浮点数深度解析:从IEEE 754到Q格式的工程实践
  • 算法面试——字符串:反转、最长回文、字符串解码
  • 2026 年更新:嘉兴可靠的纤维增强水泥压力板生产厂家怎么联系,这种装修材料凭什么成为工装和家装的隐形刚需?-欧拉德建材 - 鉴选官
  • 微服务拆完才是开始:Saga、Outbox 与渐进迁移方案
  • HarmonyOS ArkTS API 24+ 实战:登录后用户信息如何全局流转,token 存哪里、页面怎么拿当前用户
  • deepseek-v4-flash 正式版 68 元免费体验
  • 嵌入式处理器流水线原理:从基础概念到性能优化
  • Seata分布式事务:原理、实践与性能优化
  • 安卓模拟器本地OCR集成方案:基于PaddleOCR与按键精灵的自动化脚本优化
  • 游戏玩家30天无痛掌握Python:从零到实战项目全攻略
  • 2026年8月自贡市移动200M单宽带申请避坑与实测攻略 - 找卡家园
  • 2026 年当下,汕头比较好的新能源电车托运企业找哪家,把车远迁老家居然比开回去还划算?这玩意儿的门道我居然现在才懂!-盛世华航轿车托运 - 企业推荐官【认证官方】
  • 如何彻底掌控你的微信聊天记录:三步实现个人数据备份与管理的终极方案
  • 2026广东纤维素抑尘剂厂家移动电话汇总 - 品牌排行榜
  • Unity协程全解析:从原理到实战,掌握异步编程核心技能
  • Modbus RTU协议深度解析:从通信原理到工业现场实战应用
  • 算法面试——深度优先搜索:全排列、组合总和、岛屿数量
  • 舒尔补:矩阵降维与条件化的核心工具及其应用
  • Linux 权限管理——chmod、chown、sudo、ACL
  • 2026年8月山东省济南市移动融合宽带办理指南 - 找卡家园
  • H3c RC3000 开启 Telnet 及解除锁网
  • 2026 年当下,上海知名的异型雕塑平台推荐几家,小区角落突然冒出来的这玩意儿,竟让路过的人都停下了脚步?-宏观雕塑 - 行业推荐官【认证】
  • 页面置换算法深度解析:从缺页计算到系统性能调优实战
  • Autodock Vina 1.2.5 在Linux系统下的完整安装与配置指南
  • 2026年市面上耐用的101三角龙骨生产厂家联络方式 - 品牌排行榜
  • CTF杂项挑战实战:从盲文、音频频谱到图片隐写的全流程解析
  • 深入解析C++内存模型:从竞争条件到无锁编程实战
  • 2026年8月重庆市联通融合宽带怎么报装 - 找卡家园
  • NS-USBLoader终极指南:免费跨平台Switch游戏管理工具
  • 微服务边界别再凭感觉:用 DDD 识别业务边界