← 返回题目列表

命令模式中的宏命令和事务命令怎么理解?

高频 困难 第 13 / 25 题 更新于 2026/07/28
命令模式宏命令事务命令批处理补偿

简化版

宏命令是把多个命令组合成一个更大的命令,一次执行多个操作;事务命令关注一组操作的成功、失败和回滚或补偿。两者都利用了命令对象可组合、可记录、可统一调度的特点。

详细版

宏命令常见形式:

MacroCommand
  ├── Command A
  ├── Command B
  └── Command C

执行宏命令时,按顺序执行子命令。

如果需要撤销,可以反向调用已成功命令的 undo()

execute: A -> B -> C
undo:    C -> B -> A

事务命令比宏命令更关注一致性。执行过程中如果某一步失败,需要决定是整体回滚、补偿已完成步骤,还是记录失败等待人工处理。

在数据库本地事务里可以依赖事务回滚;在分布式业务里通常要设计补偿命令,类似 Saga 的思想。

完整版教学

一、宏命令是什么

宏命令就是一个命令里包含多个子命令。

class MacroCommand implements Command {
    private final List<Command> commands = new ArrayList<>();

    void add(Command command) {
        commands.add(command);
    }

    public void execute() {
        for (Command command : commands) {
            command.execute();
        }
    }

    public void undo() {
        for (int i = commands.size() - 1; i >= 0; i--) {
            commands.get(i).undo();
        }
    }
}

它把一组操作当成一个整体。

例如“一键下班”宏命令:

  • 关灯;
  • 关空调;
  • 锁门;
  • 打开安防。

用户只点一次按钮,系统执行多个命令。

二、为什么撤销要反向执行

假设执行顺序是:

A 创建目录
B 创建文件
C 写入内容

撤销时必须反过来:

C 删除内容
B 删除文件
A 删除目录

如果先撤销 A,目录里还有文件,可能删除失败。

所以宏命令撤销通常遵循后进先出。

三、事务命令关注失败后的状态一致性

宏命令只说明多个命令组合执行,但没有天然保证全部成功。

如果执行到第三步失败:

A 成功
B 成功
C 失败

系统必须决定怎么处理 A 和 B。

常见策略:

  • 如果在同一个数据库事务中,直接回滚;
  • 如果是跨系统操作,执行补偿命令;
  • 如果无法自动补偿,记录异常并人工介入;
  • 如果允许部分成功,明确记录最终状态。

四、本地事务命令示例

如果多个操作都在同一个数据库里,可以用事务包裹:

class CreateOrderCommand implements Command {
    public void execute() {
        transactionTemplate.execute(status -> {
            orderService.create();
            stockService.lock();
            couponService.freeze();
            return null;
        });
    }
}

这种场景里,数据库事务负责原子性,命令对象负责封装请求。

五、分布式场景更接近补偿事务

如果命令涉及多个系统:

订单服务创建订单
库存服务锁库存
优惠券服务冻结优惠券
支付服务创建支付单

它们无法靠一个本地数据库事务保证一致性。

这时可以为每个动作设计补偿命令:

锁库存 -> 解锁库存
冻结优惠券 -> 解冻优惠券
创建支付单 -> 关闭支付单

执行失败时,按相反顺序执行已完成动作的补偿命令。

六、事务命令要注意幂等和可观测性

补偿命令可能重复执行,也可能执行失败。

工程里要补充:

  • 命令唯一 ID;
  • 子命令执行状态;
  • 重试次数;
  • 补偿日志;
  • 幂等校验;
  • 告警和人工处理入口。

命令模式可以帮助组织这些动作,但不能替代分布式事务方案。

七、常见误区与追问

宏命令强调组合执行,事务命令强调失败后的原子性或补偿,两者不能直接画等号。设宏命令依次执行 A、B、C,若 C 失败,撤销顺序应为 B、A,而不是 A、B;已经产生的外部通知还可能无法真正回滚。数据库事务可以提供本地 ACID,跨服务场景通常只能设计可重试的补偿动作。

检查维度判定依据
宏命令组合多个命令,关注批量与顺序
事务命令关注失败边界、回滚或补偿

记忆钩子:正向 A→B→C,补偿要 C→B→A,并且每一步都要允许重入。

  • 误区:宏命令天然具有事务性。 组合只定义执行顺序,不会自动提供隔离、原子提交或失败回滚。
  • 追问:为什么补偿要逆序执行? 后执行步骤通常依赖前一步结果,逆序更符合依赖栈的拆除顺序。
  • 误区:补偿动作一定能恢复原状。 邮件发送、第三方扣款等副作用可能只能追加冲正记录,不能抹除历史。
  • 追问:部分补偿失败怎么办? 保存步骤状态并允许幂等重试,超过阈值后转人工处理。
  • 追问:何时直接用数据库事务? 所有步骤位于同一数据库事务边界且耗时可控时,本地事务通常更简单可靠。

八、加强记忆

宏命令是把多个命令组合成一个命令,事务命令是在组合执行的基础上关注失败、回滚和补偿。执行按正序,撤销或补偿通常按逆序;跨系统场景要靠幂等、状态记录和补偿机制保证最终可控。