命令模式与宏命令之间的联系

命令模式与宏命令的关系可以概括为「整体与部分」的组合关系,具体来说:


1. 层级关系

  • 命令模式 是基础设计模式,提供单个命令的封装机制
  • 宏命令 是命令模式的扩展应用,通过组合多个命令形成复合命令,本质上是一个组合模式(Composite Pattern)与命令模式的结合。

2. 设计意图对比

命令模式 宏命令
核心目标 解耦请求发送者与执行者 批量处理多个命令
关注点 单个命令的生命周期管理 命令集合的编排与协同执行
典型应用 按钮点击操作、撤销/重做单个步骤 一键执行复杂操作流、事务处理
扩展性体现 支持不同的具体命令实现 支持动态增删子命令

3. 结构关系图解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
          ┌───────────────┐
│ Command │
│ (接口/抽象类) │
└──────┬────────┘

┌───────┴───────┐
│ │
┌─────────────────┐ ┌─────────────────┐
│ ConcreteCommand │ │ MacroCommand │
│ (具体单个命令) │ │ (复合命令容器) │
└─────────────────┘ └───────┬─────────┘

┌───────┴───────┐
│ │
┌─────────────┐ ┌─────────────┐
│ Command A │ │ Command B │
└─────────────┘ └─────────────┘

4. 关键协作方式

(1) 宏命令如何扩展命令模式

  • 组合模式的应用:宏命令自身实现Command接口,同时内部维护一个Command集合
  • 透明性处理:对调用者而言,执行宏命令与执行普通命令无感知差异
  • 递归执行:宏命令的execute()方法遍历调用所有子命令的execute()

(2) 典型交互流程

1
2
3
4
5
6
[调用者] → 触发宏命令.execute()


宏命令遍历子命令集合 → 依次执行 command1.execute()
command2.execute()
command3.execute()

5. 实战案例:支持撤销的事务操作

场景需求

  • 用户执行「批量删除文件」操作
  • 需要支持整体撤销(回滚所有删除操作)
  • 若任意子命令失败,自动回滚已执行的命令

代码实现要点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 增强的宏命令(支持事务回滚)
class TransactionMacroCommand implements Command {
private List<Command> commands = new ArrayList<>();
private Stack<Command> executedCommands = new Stack<>();

public void addCommand(Command cmd) {
commands.add(cmd);
}

@Override
public void execute() {
executedCommands.clear();
try {
for (Command cmd : commands) {
cmd.execute();
executedCommands.push(cmd); // 记录已执行的命令
}
} catch (Exception e) {
undoAll(); // 出现异常时回滚
throw e;
}
}

public void undoAll() {
while (!executedCommands.isEmpty()) {
Command cmd = executedCommands.pop();
if (cmd instanceof UndoableCommand) {
((UndoableCommand) cmd).undo();
}
}
}
}

// 可撤销的命令接口
interface UndoableCommand extends Command {
void undo();
}

// 具体可撤销命令示例
class DeleteFileCommand implements UndoableCommand {
private File file;
private boolean backupExists = false;

public void execute() {
if (file.exists()) {
createBackup(); // 创建备份以便撤销
file.delete();
}
}

public void undo() {
if (backupExists) restoreFromBackup(); // 恢复文件
}
}

6. 二者的本质联系

  • 相同基因:宏命令完全遵循命令模式的核心思想(将请求封装为对象)
  • 能力增强:宏命令通过组合模式赋予了命令模式批量操作嵌套执行的能力
  • 设计演进
    单一命令可撤销命令宏命令 体现了设计模式的渐进式抽象过程

总结:选择使用宏命令的时机

当你的系统出现以下特征时,应考虑在命令模式基础上引入宏命令:

  1. 需要将多个关联操作原子化执行(如数据库事务)
  2. 存在重复操作序列需要抽象(如自动化脚本)
  3. 要求实现层级撤销(如 Photoshop 的批量操作回退)
  4. 需要动态组装操作流(如工作流引擎)