命令模式
欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。
# 简介
在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知 道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,可以使用命令模式来进行设计。
命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
通俗易懂的理解:将军发布命令,士兵去执行。
其中有几个角色: 将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。 Invoker 是调用者(将军),Receiver 是被调用者(士兵), MyCommand 是命令,实现了 Command 接口,持有接收对象。
# 命令模式
# 代码示例
/**
* 命令接口
*/
public interface Command {
// 执行动作(操作)
public void execute();
// 撤销动作(操作)
public void undo();
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
/**
* 关灯命令
*/
@AllArgsConstructor
public class LightOffCommand implements Command {
// 接收者
private LightReceiver lightReceiver;
@Override
public void execute() {
lightReceiver.off();
}
@Override
public void undo() {
lightReceiver.open();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 开灯命令
*/
@AllArgsConstructor
public class LightOnCommand implements Command {
// 接收者
private LightReceiver lightReceiver;
@Override
public void execute() {
lightReceiver.open();
}
@Override
public void undo() {
lightReceiver.off();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 电灯
*/
public class LightReceiver {
public void open() {
System.out.println(" 电灯打开了.. ");
}
public void off() {
System.out.println(" 电灯关闭了.. ");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
/**
* 遥控器
*/
public class RemoteController {
// 开按钮命令
Command[] onCommands;
Command[] offCommands;
// 撤销命令
Command undoCommand;
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
/**
* 给按钮设置命令
* @param no 第几行
* @param onCommand 开启命令
* @param offCommand 关闭命令
*/
public void setCommand(int no, Command onCommand, Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
/**
* 当打开的按钮被点击
* @param no 第几行
*/
public void onButtonClicked(int no) {
// 找到你按下的开的按钮, 并调用对应方法
onCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = onCommands[no];
}
/**
* 当关闭打开的按钮被点击
* @param no 第几行
*/
public void offButtonClicked(int no) {
// 找到你按下的开的按钮, 并调用对应方法
offCommands[no].execute();
// 记录这次的操作,用于撤销
undoCommand = offCommands[no];
}
/**
* 当撤销的按钮被点击
* @param no 第几行
*/
public void undoButtonClicked(int no) {
undoCommand.undo();
}
}
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
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
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
// 创建电灯
LightReceiver lightReceiver = new LightReceiver();
// 创建电灯相关的开关命令
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 创建一个遥控器
RemoteController remoteController = new RemoteController();
remoteController.setCommand(0, lightOnCommand, lightOffCommand);
System.out.println("点击遥控器开按钮");
remoteController.onButtonClicked(0);
System.out.println("点击遥控器关按钮");
remoteController.offButtonClicked(0);
System.out.println("点击遥控器撤销按钮");
remoteController.undoButtonClicked(0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Spring 中 JdbcTemplate 命令模式的使用方式
@Override
@Nullable
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
/**
* 命令的具体实现类
*/
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
@Override
@Nullable
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
return rse.extractData(rs);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
@Override
public String getSql() {
return sql;
}
}
// 执行命令
return execute(new QueryStatementCallback(), true);
}
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
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
- 这个
StatementCallback
接口就相当于上面的命令接口。 - 图中的 5 个实现类相当于具体的命令实现类(LightOnCommand、LightOffCommand、NoCommand)。
- execute () 就相当于 Command 中的调用方法,直接调用的是 StatementCallback 中的 doInStatement () 方法。
query
方法成为一个更高层次的操作,对于查询的具体执行方式并不关心。
# 说明
优点
- 解耦发起者和接收者: 命令模式将发起者和接收者解耦,发起者不需要知道接收者的具体类别,只需要知道命令即可。这使得系统更加灵活,易于扩展和维护。
- 支持撤销操作: 可以比较容易地实现命令的撤销和重做,因为每个具体命令对象都包含了执行和撤销操作的方法。
- 容易扩展新命令: 增加新的命令类很容易,不需要修改已有的代码,符合开闭原则。
- 支持事务: 命令模式可以用于实现事务的管理,即多个命令组合成一个复合命令,执行时保证事务的一致性。
- 队列请求: 可以将命令对象存储在队列中,实现对请求的排队、延迟执行和重试等功能。
缺点
- 类膨胀: 每个具体命令都需要一个单独的类,如果命令类很多,可能会导致类膨胀。
- 理解和实现可能有难度: 对于初学者来说,理解命令模式可能会有一定的难度,特别是涉及到撤销和重做的情况。
- 可能引入不必要的复杂性: 对于简单的场景,使用命令模式可能会引入不必要的复杂性,增加系统的维护成本。
上次更新: 2025/04/12, 05:37:39