命令模式详解

什么是命令模式?

命令模式(Command Pattern)是一种行为型设计模式,它将请求封装为对象,从而使你可以用不同的请求参数化客户端,支持可撤销操作、队列化请求以及日志记录等功能。

核心概念

  • Command(命令):定义执行操作的接口,包含一个执行方法。
  • ConcreteCommand(具体命令):实现命令接口,绑定接收者和动作,调用接收者的方法执行操作。
  • Receiver(接收者):执行命令的实际对象,知道如何执行与命令相关的操作。
  • Invoker(调用者):要求命令执行请求,持有命令对象。
  • Client(客户端):创建具体命令对象并设置其接收者。

命令模式的UML图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+----------------+       +----------------+
| Command |<------| Invoker |
+----------------+ +----------------+
| +execute() | | -command |
| +undo() | | +setCommand() |
+----------------+ | +invoke() |
^ +----------------+
| |
+----------------+ +----------------+
| ConcreteCommand| | Client |
+----------------+ +----------------+
| -receiver | | +createCommand()|
| +execute() | | +run() |
| +undo() | +----------------+
+----------------+ |
| |
v |
+----------------+ +----------------+
| Receiver |<------| |
+----------------+ +----------------+
| +action() |
+----------------+

命令模式的优缺点

优点

  1. 解耦:将请求的发送者和接收者解耦,发送者不需要知道接收者的具体实现。
  2. 可扩展性:可以轻松添加新的命令,而不需要修改现有代码。
  3. 可撤销操作:支持命令的撤销和重做操作。
  4. 宏命令:可以组合多个命令为一个复合命令。
  5. 队列化请求:可以将命令放入队列中执行,支持异步操作。
  6. 日志记录:可以记录命令的执行历史,支持崩溃恢复。

缺点

  1. 类数量增加:每个命令都需要创建一个类,可能会导致类数量增加。
  2. 复杂度增加:命令模式的实现可能会增加系统的复杂度。
  3. 性能开销:命令对象的创建和管理可能会带来一定的性能开销。

命令模式的实现

C++ 实现

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <iostream>
#include <vector>
#include <string>

// 接收者类
class Receiver {
public:
void action(const std::string& message) {
std::cout << "Receiver: " << message << std::endl;
}
};

// 命令接口
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};

// 具体命令
class ConcreteCommand : public Command {
private:
Receiver* receiver;
std::string message;
std::string previousMessage;

public:
ConcreteCommand(Receiver* receiver, const std::string& message)
: receiver(receiver), message(message) {}

void execute() override {
previousMessage = "Previous state"; // 模拟保存前一状态
receiver->action(message);
}

void undo() override {
receiver->action(previousMessage);
}
};

// 调用者类
class Invoker {
private:
Command* command;
std::vector<Command*> history;

public:
~Invoker() {
for (Command* cmd : history) {
delete cmd;
}
}

void setCommand(Command* cmd) {
command = cmd;
}

void invoke() {
if (command) {
command->execute();
history.push_back(command);
command = nullptr; // 避免重复执行
}
}

void undo() {
if (!history.empty()) {
Command* lastCommand = history.back();
lastCommand->undo();
history.pop_back();
}
}
};

// 客户端
class Client {
private:
Invoker* invoker;
Receiver* receiver;

public:
Client(Invoker* invoker, Receiver* receiver)
: invoker(invoker), receiver(receiver) {}

void createAndExecuteCommand(const std::string& message) {
Command* command = new ConcreteCommand(receiver, message);
invoker->setCommand(command);
invoker->invoke();
}

void undoLastCommand() {
invoker->undo();
}
};

// 测试代码
int main() {
// 创建接收者
Receiver receiver;

// 创建调用者
Invoker invoker;

// 创建客户端
Client client(&invoker, &receiver);

// 执行命令
std::cout << "=== Executing Command 1 ===" << std::endl;
client.createAndExecuteCommand("Execute command 1");

std::cout << "\n=== Executing Command 2 ===" << std::endl;
client.createAndExecuteCommand("Execute command 2");

// 撤销命令
std::cout << "\n=== Undoing Last Command ===" << std::endl;
client.undoLastCommand();

return 0;
}

Java 实现

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import java.util.ArrayList;
import java.util.List;

// 接收者类
class Receiver {
public void action(String message) {
System.out.println("Receiver: " + message);
}
}

// 命令接口
interface Command {
void execute();
void undo();
}

// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
private String message;
private String previousMessage;

public ConcreteCommand(Receiver receiver, String message) {
this.receiver = receiver;
this.message = message;
}

@Override
public void execute() {
previousMessage = "Previous state"; // 模拟保存前一状态
receiver.action(message);
}

@Override
public void undo() {
receiver.action(previousMessage);
}
}

// 调用者类
class Invoker {
private Command command;
private List<Command> history = new ArrayList<>();

public void setCommand(Command cmd) {
this.command = cmd;
}

public void invoke() {
if (command != null) {
command.execute();
history.add(command);
command = null; // 避免重复执行
}
}

public void undo() {
if (!history.isEmpty()) {
Command lastCommand = history.remove(history.size() - 1);
lastCommand.undo();
}
}
}

// 客户端
class Client {
private Invoker invoker;
private Receiver receiver;

public Client(Invoker invoker, Receiver receiver) {
this.invoker = invoker;
this.receiver = receiver;
}

public void createAndExecuteCommand(String message) {
Command command = new ConcreteCommand(receiver, message);
invoker.setCommand(command);
invoker.invoke();
}

public void undoLastCommand() {
invoker.undo();
}
}

// 测试代码
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建接收者
Receiver receiver = new Receiver();

// 创建调用者
Invoker invoker = new Invoker();

// 创建客户端
Client client = new Client(invoker, receiver);

// 执行命令
System.out.println("=== Executing Command 1 ===");
client.createAndExecuteCommand("Execute command 1");

System.out.println("\n=== Executing Command 2 ===");
client.createAndExecuteCommand("Execute command 2");

// 撤销命令
System.out.println("\n=== Undoing Last Command ===");
client.undoLastCommand();
}
}

Python 实现

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 接收者类
class Receiver:
def action(self, message):
print(f"Receiver: {message}")

# 命令接口
class Command:
def execute(self):
pass

def undo(self):
pass

# 具体命令
class ConcreteCommand(Command):
def __init__(self, receiver, message):
self.receiver = receiver
self.message = message
self.previous_message = ""

def execute(self):
self.previous_message = "Previous state" # 模拟保存前一状态
self.receiver.action(self.message)

def undo(self):
self.receiver.action(self.previous_message)

# 调用者类
class Invoker:
def __init__(self):
self.command = None
self.history = []

def set_command(self, command):
self.command = command

def invoke(self):
if self.command:
self.command.execute()
self.history.append(self.command)
self.command = None # 避免重复执行

def undo(self):
if self.history:
last_command = self.history.pop()
last_command.undo()

# 客户端
class Client:
def __init__(self, invoker, receiver):
self.invoker = invoker
self.receiver = receiver

def create_and_execute_command(self, message):
command = ConcreteCommand(self.receiver, message)
self.invoker.set_command(command)
self.invoker.invoke()

def undo_last_command(self):
self.invoker.undo()

# 测试代码
if __name__ == "__main__":
# 创建接收者
receiver = Receiver()

# 创建调用者
invoker = Invoker()

# 创建客户端
client = Client(invoker, receiver)

# 执行命令
print("=== Executing Command 1 ===")
client.create_and_execute_command("Execute command 1")

print("\n=== Executing Command 2 ===")
client.create_and_execute_command("Execute command 2")

# 撤销命令
print("\n=== Undoing Last Command ===")
client.undo_last_command()

C# 实现

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System;
using System.Collections.Generic;

// 接收者类
class Receiver {
public void Action(string message) {
Console.WriteLine($"Receiver: {message}");
}
}

// 命令接口
interface ICommand {
void Execute();
void Undo();
}

// 具体命令
class ConcreteCommand : ICommand {
private Receiver receiver;
private string message;
private string previousMessage;

public ConcreteCommand(Receiver receiver, string message) {
this.receiver = receiver;
this.message = message;
}

public void Execute() {
previousMessage = "Previous state"; // 模拟保存前一状态
receiver.Action(message);
}

public void Undo() {
receiver.Action(previousMessage);
}
}

// 调用者类
class Invoker {
private ICommand command;
private List<ICommand> history = new List<ICommand>();

public void SetCommand(ICommand cmd) {
this.command = cmd;
}

public void Invoke() {
if (command != null) {
command.Execute();
history.Add(command);
command = null; // 避免重复执行
}
}

public void Undo() {
if (history.Count > 0) {
ICommand lastCommand = history[history.Count - 1];
history.RemoveAt(history.Count - 1);
lastCommand.Undo();
}
}
}

// 客户端
class Client {
private Invoker invoker;
private Receiver receiver;

public Client(Invoker invoker, Receiver receiver) {
this.invoker = invoker;
this.receiver = receiver;
}

public void CreateAndExecuteCommand(string message) {
ICommand command = new ConcreteCommand(receiver, message);
invoker.SetCommand(command);
invoker.Invoke();
}

public void UndoLastCommand() {
invoker.Undo();
}
}

// 测试代码
class Program {
static void Main(string[] args) {
// 创建接收者
Receiver receiver = new Receiver();

// 创建调用者
Invoker invoker = new Invoker();

// 创建客户端
Client client = new Client(invoker, receiver);

// 执行命令
Console.WriteLine("=== Executing Command 1 ===");
client.CreateAndExecuteCommand("Execute command 1");

Console.WriteLine("\n=== Executing Command 2 ===");
client.CreateAndExecuteCommand("Execute command 2");

// 撤销命令
Console.WriteLine("\n=== Undoing Last Command ===");
client.UndoLastCommand();
}
}

命令模式的使用场景

1. GUI 应用

  • 按钮点击:每个按钮对应一个命令
  • 菜单操作:每个菜单项对应一个命令
  • 快捷键:每个快捷键对应一个命令

2. 撤销/重做操作

  • 文本编辑器:撤销/重做文本修改
  • 图形编辑器:撤销/重做图形操作
  • 表单操作:撤销/重做表单修改

3. 队列化请求

  • 任务队列:将命令放入队列中异步执行
  • 线程池:线程池执行命令队列
  • 消息队列:消息队列中的消息作为命令执行

4. 事务处理

  • 数据库事务:将多个操作封装为一个命令,支持回滚
  • 业务事务:将多个业务操作封装为一个命令,支持回滚

5. 远程方法调用

  • RPC:将远程调用封装为命令
  • Web 服务:将 HTTP 请求封装为命令
  • 消息传递:将消息封装为命令

6. 宏命令

  • 批处理操作:将多个命令组合为一个复合命令
  • 快捷键组合:将多个操作绑定到一个快捷键
  • 工作流:将工作流步骤封装为命令链

7. 其他场景

  • 日志记录:记录命令执行历史,支持崩溃恢复
  • 游戏开发:游戏中的操作命令,支持撤销/重做
  • 配置管理:配置变更的命令化,支持回滚

适合使用命令模式的问题

1. 当需要将请求的发送者和接收者解耦时

例如:

  • GUI 组件不需要知道具体的业务逻辑实现
  • 业务逻辑层不需要知道具体的底层实现

2. 当需要支持撤销和重做操作时

例如:

  • 文本编辑器的撤销/重做功能
  • 图形编辑器的撤销/重做功能

3. 当需要将请求队列化或异步执行时

例如:

  • 任务调度系统
  • 消息队列系统

4. 当需要将多个操作组合为一个复合操作时

例如:

  • 宏命令
  • 事务操作

5. 当需要记录命令执行历史时

例如:

  • 日志系统
  • 崩溃恢复系统

命令模式的变体

1. 简单命令 vs 复合命令

  • 简单命令:单个操作的命令
  • 复合命令:多个操作的组合命令(宏命令)

2. 有状态命令 vs 无状态命令

  • 有状态命令:命令对象包含执行所需的状态
  • 无状态命令:命令对象不包含状态,状态由接收者管理

3. 同步命令 vs 异步命令

  • 同步命令:命令执行是同步的
  • 异步命令:命令执行是异步的

4. 可撤销命令 vs 不可撤销命令

  • 可撤销命令:支持 undo 操作
  • 不可撤销命令:不支持 undo 操作

5. 命令处理器模式

命令处理器管理命令的执行、队列化和调度

实际应用案例

1. Java Swing/AWT

Java Swing 中的 Action 接口和 AbstractAction 类是命令模式的应用。

2. 遥控器

电视、空调等遥控器的设计使用了命令模式。

3. 游戏开发

游戏中的操作(移动、攻击、跳跃等)通常使用命令模式实现。

4. 事务系统

数据库事务、业务事务等使用命令模式实现。

5. 工作流系统

工作流中的每个步骤可以看作是一个命令。

6. 批处理系统

批处理作业可以看作是一系列命令的组合。

代码优化建议

1. 使用命令工厂

使用工厂方法或抽象工厂模式创建命令对象,提高代码的可维护性。

2. 使用命令池

对于频繁创建的命令对象,考虑使用对象池模式,减少对象创建的开销。

3. 优化命令的序列化

对于需要持久化或网络传输的命令,优化序列化和反序列化过程。

4. 合理处理异常

在命令的 execute() 和 undo() 方法中添加异常处理逻辑,确保命令的执行和撤销不会因异常而中断。

5. 考虑线程安全性

如果命令在多线程环境中执行,确保命令的线程安全性。

6. 使用装饰器模式增强命令

使用装饰器模式为命令添加额外的功能,如日志记录、性能监控等。

7. 合理设计命令的粒度

命令的粒度应该适中,过大的命令会降低灵活性,过小的命令会增加系统复杂度。

总结

命令模式是一种非常实用的设计模式,它通过将请求封装为对象,实现了请求发送者和接收者的解耦,支持撤销/重做、队列化请求、事务处理等功能。

命令模式在 GUI 应用、撤销/重做操作、队列化请求、事务处理、远程方法调用等场景中有着广泛的应用。通过合理使用命令模式,可以提高系统的灵活性、可扩展性和可维护性。

在实际开发中,当你遇到需要解耦请求发送者和接收者、支持撤销/重做操作、队列化请求或组合多个操作的场景时,不妨考虑使用命令模式。

记住,命令模式的核心是将行为封装为对象,通过这种方式,可以实现更灵活、更可扩展的系统设计。