命令模式的核心是将请求封装为对象以解耦调用者与执行者,并支持撤销、重做等扩展;java实现需定义含execute()和undo()的command接口,命令类保存执行前状态,接收者专注业务逻辑,invoker管理命令历史栈与队列。
命令模式的核心是把“请求”变成对象,让调用者与执行者解耦,同时为撤销、重做、日志记录、队列调度等扩展能力打下基础。在 java 中实现时,关键在于定义统一的命令接口、封装具体操作逻辑、维护命令历史栈,并通过上下文协调执行与回滚。
定义 Command 接口并支持撤销
命令接口需包含 execute() 和 undo() 两个方法。注意:undo 不是 execute 的逆向自动推导,而是由命令自身保存必要状态(如原始值、目标对象引用等),确保可精准回退。
- 避免在 execute 中直接修改业务对象状态后再试图“猜”怎么还原;应在执行前缓存关键字段(例如余额、开关状态、文本内容)
- 若命令涉及多个对象或复杂流程,可在 undo() 中调用反向操作方法,而非硬编码恢复逻辑
- 示例:一个设置文本颜色的命令,应保存执行前的颜色值,而不是在 undo 里写“设回默认色”
实现具体命令类并管理执行上下文
每个业务动作对应一个 ConcreteCommand 类,它持有接收者(Receiver)引用,并在 execute/undo 中委托调用。接收者是真正干活的对象(如 Document、Light、BankAccount),不感知命令模式的存在。
- 接收者尽量保持简单:只暴露明确的业务方法(如 save()、turnOn()、withdraw(amount))
- 命令类负责组装参数、判断前置条件、捕获执行结果——这些不属于接收者的职责
- 避免命令类过度膨胀:若逻辑复杂,可拆出私有辅助方法,或引入参数对象(CommandData)封装输入
构建 Invoker 支持队列、撤销与重做
Invoker 是命令的调度中心,它不关心命令做什么,只负责触发、排队、记录历史。典型结构包含三个集合:
-
commandHistory:Stack
,记录已执行命令,用于 undo -
redoStack:Stack
,保存被 undo 的命令,供 redo 使用 -
commandQueue(可选):Queue
,支持异步批量执行或延迟调度
每次 execute 后将命令压入 history;undo 时弹出 history 顶部并调用其 undo(),再压入 redoStack;redo 则从 redoStack 弹出并重新 execute。
处理不可撤销操作与边界情况
不是所有操作都天然支持 undo(如 HTTP 外部调用、文件删除、消息发送)。这时需在设计阶段明确约定:
- 标记命令是否可撤销(isUndoable() 方法),Invoker 跳过对不可撤销命令的 undo 记录
- 对外部系统操作,改用补偿型命令(Compensating Command):比如“下单”后跟“取消订单”,而非试图撤回已发的网络请求
- 避免 undo 堆栈无限增长:可限制最大历史长度,或提供 clearHistory() 接口,在业务关键点主动截断
不复杂但容易忽略的是状态快照时机和线程安全。多线程环境下,history 和 redoStack 需用线程安全容器(如 ConcurrentLinkedDeque)或加锁;而状态快照必须在 execute 执行前完成,否则 undo 可能失效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











