命令模式
# 一、概述
命令(Command)模式将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操作。
在命令模式中,会将一个请求封装为一个对象,以便使用不同的参数来表示不同的请求,且该请求支持撤销操作。
# 1.1 解决了什么问题
命令模式最大的特点是解耦。
小李到公司楼下的兰州牛肉面(是兰州牛肉面,不是兰州拉面)吃饭,整个就餐的过程是这样的:
- 小李告诉服务员自己要吃什么(可能是二细、大宽、也可能是炒拉条)
- 服务员告诉后厨,来一碗二细、或者大宽、扩折炒拉条
- 后厨用自己的方式维护了一个出餐列表
- 后厨根据不同的订单进行制作,并将制作好的菜品放到窗口由小李自己获取
这整个流程等其实就是命令模式,它很通过服务员完美解耦了小李和后厨,小李只需要发送命令,他不需要关心命令的具体执行者是谁,而且在命令被具体执行之前都可以撤销。
# 1.2 解决方案
通过在客户端和命令执行者之间增加调用者做到解耦。
虽然命令模式实现了解耦,但同时会增加代码量,因为每个命令都需要创建一个类。
# 二、实现方式
# 2.1 角色
- Invoker:命令调用者,负责对请求进行初始化,其中必须包含一个成员变量来存储对命令对象的引用。调用者不会直接创建命令对象,而是通过构造函数从客户端获得预先生成的命令。
- Receiver:命令执行者,负责完成实际的工作。
- Command:抽象的命令接口,通常只要䘝执行命令的方法。
- Concrete Command:具体的命令,实现 Command 接口,每个命令都对应一个具体的命令类。
- Client:创建一个具体的命令。
# 2.2 代码
Command 接口:
public interface Command {
void execute();
}
1
2
3
2
3
Concrete Command:
public class ErxiCommand implements Command {
private BeefNoodlesWorker worker;
public ErxiCommand(BeefNoodlesWorker worker) {
this.worker = worker;
}
@Override
public void execute() {
this.worker.makeErxi();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
public class DakuanCommand implements Command {
private BeefNoodlesWorker worker;
public DakuanCommand(BeefNoodlesWorker worker) {
this.worker = worker;
}
@Override
public void execute() {
this.worker.makeDakuan();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
public class ChaolatiaoCommand implements Command {
private ChaomianWorker worker;
public ChaolatiaoCommand(ChaomianWorker worker) {
this.worker = worker;
}
@Override
public void execute() {
this.worker.makeChaolatiao();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Receiver:
/**
* 牛肉面厨师
*/
public class BeefNoodlesWorker {
public void makeErxi() {
System.out.println("二细制作完成");
}
public void makeDakuan() {
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 ChaomianWorker {
public void makeChaolatiao() {
System.out.println("炒拉条制作完成");
}
public void makeChaomian() {
System.out.println("炒面制作完成");
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
命令调用者:
public class Invoker {
private final List<Command> commands = new ArrayList<>();
public void addCommand(Command command) {
commands.add(command);
System.out.println("添加命令成功:" + command.getClass().getSimpleName());
}
public void executeCommand() {
for (Command command : commands) {
command.execute();
}
}
public void UndoCommand(Command command) {
if (commands.remove(command)) {
System.out.println("撤销命令成功:" + command.getClass().getSimpleName());
} else {
System.out.println("撤销命令失败:" + command.getClass().getSimpleName());
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
客户端:
public class CommandTest {
public static void main(String[] args) {
// 对应服务员
Invoker invoker = new Invoker();
// 命令执行者
BeefNoodlesWorker beefNoodlesWorker = new BeefNoodlesWorker();
ChaomianWorker chaomianWorker = new ChaomianWorker();
// 构建具体的命令,每个命令都会绑定具体的执行者
ErxiCommand erxiCommand = new ErxiCommand(beefNoodlesWorker);
DakuanCommand dakuanCommand = new DakuanCommand(beefNoodlesWorker);
ChaolatiaoCommand chaolatiaoCommand = new ChaolatiaoCommand(chaomianWorker);
// 构建执行计划
invoker.addCommand(erxiCommand);
invoker.addCommand(dakuanCommand);
invoker.addCommand(chaolatiaoCommand);
invoker.UndoCommand(dakuanCommand);
invoker.executeCommand();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
添加命令成功:ErxiCommand
添加命令成功:DakuanCommand
添加命令成功:ChaolatiaoCommand
撤销命令成功:DakuanCommand
二细制作完成
炒拉条制作完成
1
2
3
4
5
6
2
3
4
5
6
# 三、源码中的应用
java.lang.Runnable
上次更新: 2023/11/01, 03:11:44