首页/开发/代码质量与评审速查/命令模式 Command(行为型)
设计原则与模式62 条

命令模式 Command(行为型)

把请求封装为对象:可排队、可记录、可撤销、可组合(宏命令);编辑器的撤销栈与任务队列是常见应用,命令对象只封装意图与数据。

命令模式是什么

把请求封装成对象:请求的参数、接收者、执行方式都收进一个对象里,于是可以排队、记录、重放、撤销,也可以把多个命令组合成宏命令。命令对象只封装意图与数据,真正的业务仍在接收者身上。编辑器的撤销栈、任务队列、事务脚本是它最常见的三个落点。

基本信息

意图把请求对象化:可参数化、排队、撤销
组成Command 接口 + Receiver + Invoker + Client
应用撤销重做、任务队列、事务脚本、宏命令
要点命令只封装意图,业务留在接收者

典型使用场景

  • 编辑器撤销重做:每个操作保存为命令对象,压入栈后按序回滚
  • 任务队列:命令对象可序列化后投递,由工作进程取出执行
  • 批量操作:一组命令组合成宏命令,整体执行或整体撤销
  • 操作日志与审计:命令天然带有参数,便于记录与回放
  • 延迟执行:把命令先收集起来,在事务提交或某个时机统一执行

Java 示例

Java 示例
interface Command { void execute(); void undo(); }

class BoldCommand implements Command {
    private final Document doc;
    BoldCommand(Document doc) { this.doc = doc; }

    @Override public void execute() { doc.bold(); }
    @Override public void undo()    { doc.unbold(); }   // 与 execute 对称
}

class MacroCommand implements Command {
    private final List<Command> cmds;
    MacroCommand(List<Command> cmds) { this.cmds = cmds; }

    @Override public void execute() { cmds.forEach(Command::execute); }
    @Override public void undo() {                     // 逆序回滚
        for (int i = cmds.size() - 1; i >= 0; i--) cmds.get(i).undo();
    }
}

角色职责

角色职责常见错误
Command封装请求参数与执行方式把业务规则写进命令
Receiver真正执行操作的对象被命令绕过,逻辑散在两处
Invoker触发命令、维护历史栈直接调用接收者,绕过命令
Client装配命令与接收者让命令自己去找接收者
undo 与 execute 必须对称:只记录部分状态、或回滚时漏掉副作用,会让撤销后的状态与执行前不一致,这类问题往往在连续多次撤销后才暴露,测试要覆盖多次往返。
命令对象持有业务逻辑是本模式的常见误用:接收者已有的能力被复制一份进命令,之后修改规则要改两处。命令应当薄到可以只看参数就明白它会做什么。

易错点

  • undo 与 execute 不对称:状态回滚不完整,连续撤销后数据错乱
  • 命令对象持有业务逻辑:与接收者职责重叠,规则改一处漏一处
  • 宏命令不逆序回滚:依赖顺序的操作撤销顺序错误,结果不可预期
  • 命令栈不设上限:长时间运行会持续占用内存,长会话场景需要裁剪或合并

数据来源与参考

  • 《设计模式》(GoF)· Command
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌