基本概念

命令模式:一种行为型设计模式,它将“请求”封装为对象,使发送请求的客户端与执行请求的接收者解耦。通过这种封装,可以让请求参数化、排队执行、记录日志或支持撤销操作。

核心结构

  1. 命令接口(Command):定义执行命令的方法(如execute())。
  2. 具体命令(Concrete Command):实现命令接口,绑定接收者和具体操作。
  3. 接收者(Receiver):执行命令的具体对象,知道如何完成命令对应的操作。
  4. 调用者(Invoker):持有命令对象,通过调用命令的方法来触发请求。
  5. 客户端(Client):创建具体命令并设置其接收者,将命令交给调用者。

示例说明

场景:遥控器控制家电

  1. 命令接口
1
2
3
4
5
6
7

// 命令接口
interface Command {
void execute(); // 执行命令
void undo(); // 撤销命令(可选)
}

  1. 接收者:电灯
1
2
3
4
5
6
7
8
9
10
11

class Light {
void on() {
System.out.println("电灯开启");
}

void off() {
System.out.println("电灯关闭");
}
}

  1. 接收者:电视
1
2
3
4
5
6
7
8
9
10
11

class TV {
void on() {
System.out.println("电视开启");
}

void off() {
System.out.println("电视关闭");
}
}

  1. 开灯命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class LightOnCommand implements Command {
private Light light;

public LightOnCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.on();
}

@Override
public void undo() {
light.off();
}
}

  1. 关灯命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class LightOffCommand implements Command {
private Light light;

public LightOffCommand(Light light) {
this.light = light;
}

@Override
public void execute() {
light.off();
}

@Override
public void undo() {
light.on();
}
}

  1. 调用者:遥控器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class RemoteControl {
private Command command; // 单个命令槽位

// 设置要执行的命令
public void setCommand(Command command) {
this.command = command;
}

// 按下按钮执行命令
public void pressButton() {
command.execute();
}

// 按下撤销按钮
public void pressUndoButton() {
command.undo();
}
}

  1. 测试验证
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

public class Test {
public static void main(String[] args) {
// 创建接收者
Light light = new Light();
TV tv = new TV();

// 创建具体命令
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
Command tvOn = new LightOnCommand(tv); // 简化示例,实际应创建TV的命令

// 创建调用者(遥控器)
RemoteControl remote = new RemoteControl();

// 设置命令并执行
remote.setCommand(lightOn);
remote.pressButton(); // 输出:电灯开启

remote.setCommand(lightOff);
remote.pressButton(); // 输出:电灯关闭
remote.pressUndoButton(); // 输出:电灯开启
}
}

命令对象通过特定接收者上绑定一组动作来封装请求。要达到这一点,它把动作和接收者包进一个对象中,该对象仅暴露可执行的方法execute()undo(),当被调用时,调用者无法知道具体内部做了什么操作,目的已经达到。

其类图如下:

命令模式

总结

优点

  • 解耦发送者与接收者:客户端无需知道谁执行命令,调用者无需知道命令的具体实现。
  • 支持撤销操作:通过undo()方法可实现命令回滚(如文本编辑的撤销)。
  • 支持日志和事务:命令执行可记录日志,便于故障恢复或事务管理。
  • 扩展性强:新增命令只需创建新的具体命令类,符合开闭原则

缺点

  • 类数量增加:每个命令都需要独立的类,可能导致类爆炸。
  • 复杂度上升:简单场景下使用命令模式可能过度设计。

使用场景

  • 需要支持撤销/重做:如文本编辑器、绘图软件的操作历史。
  • 需要命令排队或调度:如任务调度系统、线程池中的任务封装。
  • 需要记录操作日志:如银行系统的交易记录,便于故障恢复。
  • 需要将请求参数化:如GUI框架中按钮点击事件的处理(按钮是调用者,点击是命令)。
  • 多线程场景:命令可作为独立任务提交给线程池执行。