Java命令模式通过接口定义execute/undo契约,具体类封装请求参数与状态快照,Invoker用队列统一调度并记录日志,实现请求对象化、可逆与解耦。

Java 接口中结合命令模式实现请求封装与排队处理,关键在于用接口定义统一行为契约,再通过具体实现类承载业务逻辑和状态快照,最后由调用器(Invoker)统一调度队列。这不是单纯写个 interface 就完事,而是围绕“请求即对象”这一核心,让接口成为能力声明的枢纽。
定义命令接口:聚焦 execute 与 undo 的契约
命令接口是整个模式的基石,它不关心谁执行、怎么执行,只声明“能做什么”。典型设计包含两个核心方法:
- execute():触发实际业务操作,比如创建订单、扣减库存;
- undo():提供语义安全的回滚路径,不是简单反向调用,而是基于快照恢复或触发补偿动作。
接口应保持轻量、无状态依赖,避免引入接收者(Receiver)实例或上下文对象。例如:
public interface Command {void execute();
void undo();
}
具体命令类:封装请求参数 + 必要快照数据
每个业务请求对应一个具体命令类,它实现 Command 接口,并持有执行所需的全部输入与回滚依据。重点不是“做了什么”,而是“怎么做才可逆”:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 构造时传入必要 ID、数量等参数,也建议在 execute 前查询并缓存关键状态(如扣减前库存值、修改前用户余额);
- 避免在命令中直接 new 接收者或调用静态方法,接收者应通过构造注入或 setter 注入;
- 对不可逆操作(如发短信、调支付回调),undo 方法应明确标记为“逻辑撤销”,转向人工介入或补偿流程记录。
例如:DeductInventoryCommand 在 execute 中查出原库存 100,扣 10 后记下 100;undo 时校验当前库存是否仍为 90,再恢复为 100 —— 这就规避了并发覆盖导致的数据错乱。
调用器(Invoker):用队列管理命令生命周期
Invoker 是排队与日志的核心协调者,它不实现业务,只负责“什么时候执行、往哪记、怎么撤”:
- 使用 Deque<Command> 存储已成功执行的命令,支持 LIFO 撤销;
- execute(Command cmd) 方法内:先调 cmd.execute(),成功后再将 cmd 加入队列,同时异步写入数据库或消息队列作为操作日志;
- undo() 方法:从队列弹出最后一个命令,调用其 undo(),并可选地将这次“撤销动作”本身也作为一条新命令记录,形成可追溯链。
注意:Invoker 不持有接收者,也不解析命令内容,它只做“转发+编排”,真正解耦就体现在这里。
排队与日志的落地要点
接口本身不处理排队,但它是排队可行的前提——正因为所有命令都实现了同一接口,Invoker 才能用泛型容器统一收纳、遍历、调度:
- 内存队列适合单机轻量场景(如编辑器撤销栈),用 ArrayDeque 即可;
- 分布式或高可靠场景,需将 Command 序列化后存入 Redis 队列或 Kafka 主题,执行前反序列化并校验签名;
- 日志字段建议至少包含:命令类型、时间戳、参数摘要、执行结果、traceId —— 这些不写在接口里,但在具体命令的 toString() 或专用日志方法中结构化输出。
不复杂但容易忽略

















