GoF23:Command-命令
1、命令模式
命令(Command):
将请求封装成对象,以使用不同请求对其它对象进行参数化。
- 场景:将请求的调用和实现解耦(请求 = 接收者 + 待执行动作)。
- 说明:
- 执行流程:调用者 → 命令对象 → 接收者。
- 除了命令的执行(
execute
),通常也支持撤销(undo
)、重做(redo
)。 - 遵循开闭原则。
类图
-
Receiver(接收者):真正执行请求,通常定义为抽象类型。
-
Command(命令):封装对接收者的调用。
- 持有一个 Receiver 的引用。
- 提供 execute() 执行请求。
-
ConcreteCommand(具体命令):
- 继承 Command 类。
- 实现 execute() 方法,通过接收者完成特定动作。
-
Invoker(调用者):
- 持有若干个 Command 的引用。
- 调用 Command 对象完成预期功能。
-
Client(客户端)
2、case:遥控
2.1、需求
实现遥控器控制不同家电。
- Receiver - 家电:真正执行请求。
- 电灯 Lamp:支持开关。
- 电视机 Television:支持开关,调节音量。
- Invoker - 遥控器:需要发出请求。
- 开关:多对按钮,分别对应不同家电的开关操作。
- 撤销:单个按钮,取消上一次操作。
2.2、实现命令模式
接收者 - Appliance
接口:定义公共业务方法,即家电的开关。
public interface Appliance {
void on();
void off();
}
实现类:实现公共业务方法,允许自定义业务方法。
-
电灯 Lamp:支持开关。
public class Lamp implements Appliance { @Override public void on() { System.out.println("Lamp is on"); } @Override public void off() { System.out.println("Lamp is off"); } }
-
电视机 Television:支持开关,调节音量。
public class Television implements Appliance { @Override public void on() { System.out.println("Television is on"); } @Override public void off() { System.out.println("Television is off"); } // 自定义方法 public void setVolume(int volume) { System.out.println("Television is at " + volume + " db"); } }
命令 - Command
持有一个接收者的引用(抽象类型),
定义 execute() 方法用于执行请求。
public abstract class Command {
protected Appliance appliance;
public Command(Appliance appliance) {
this.appliance = appliance;
}
public abstract void execute();
}
具体命令
LampXxx
Lamp 只有开关功能(属于接口定义的方法)
可直接通过抽象类型引用。
// 打开电灯
public class LampOnCommand extends Command {
public LampOnCommand(Appliance appliance) {
super(appliance);
}
@Override
public void execute() {
appliance.on(); // 开启
}
}
// 关闭电灯
public class LampOffCommand extends Command {
public LampOffCommand(Appliance appliance) {
super(appliance);
}
@Override
public void execute() {
appliance.off(); //关闭
}
}
TelevisionXxx
Television 有自定义功能,
需要判断并向下转型,才能正确执行。
// 打开电视
public class TelevisionOnCommand extends Command {
public StereoMusicOnCommand(Appliance appliance) {
super(appliance);
}
@Override
public void execute() {
if (appliance instanceof Television) {
Television tv = (Television) this.appliance;
Television.on(); // 开启
Television.setVolume(10); // 设置默认音量
}
}
}
// 关闭电视
public class TelevisionOffCommand extends Command {
public StereoMusicOffCommand(Appliance appliance) {
super(appliance);
}
@Override
public void execute() {
appliance.off(); // 关闭
}
}
Dummy(❗)
无意义的 Command 对象,可避免
NPE
。
public class DummyCommand extends Command {
@Override
public void execute() {
System.out.println("No Command");
}
}
调用者 - Remote
假设有 4 对开关按钮。
-
成员变量:
- 整型:开关的数量。
- 数组:对 Command 的引用,相同下标的元素代表同一对开关。
-
构造方法:初始化数组元素为空命令对象,避免 NPE。
-
setter:允许动态设置 Command 对象。
-
业务方法:代表按钮被按下。
public class Remote { private int commandCount; private Command[] onCommandList; private Command[] offCommandList; public Remote() { // 默认初始化 commandCount = 4; onCommandList = new Command[commandCount]; offCommandList = new Command[commandCount]; // 防止NPE NoCommand noCommand = new NoCommand(); for (int i = 0; i < onCommandList.length; i++) { onCommandList[i] = noCommand; offCommandList[i] = noCommand; } } // 设置命令 public void setCommand(int index, Command onCommand, Command offCommand) { onCommandList[index] = onCommand; offCommandList[index] = offCommand; } // 业务方法 public void onButtonPressed(int index) { onCommandList[index].execute(); } public void offButtonPressed(int index) { offCommandList[index].execute(); } }
客户端
基于 Receiver 创建 Command,作为参数完成 Invoker 的初始化。
// 接收者 - 家电
Lamp lamp = new Lamp();
Television tv = new Television();
// 具体命令
LampOnCommand lampOnCommand = new LampOnCommand(lamp);
LampOffCommand lampOffCommand = new LampOffCommand(lamp);
TelevisionOnCommand tvOnCommand = new TelevisionOnCommand(tv);
TelevisionOffCommand tvOffCommand = new TelevisionOffCommand(tv);
// 调用者:遥控器
Remote remote = new Remote();
remote.setCommand(0, lampOnCommand, lampOffCommand);
remote.setCommand(1, tvOnCommand, tvOffCommand);
// 发出请求
remote.onButtonPressed(0); // Lamp is on
remote.onButtonPressed(1); // Television is on
remote.onButtonPressed(2); // No Command(而不是报错NPE)
2.3、宏命令
宏命令(Macro Command)
批量执行命令。
-
继承 Command,作为命令对象。
-
成员变量:CommandList,代表需要批量执行的命令。
public class MacroCommand extends Command { private Command[] commandList; public MacroCommand(Command[] commandList) { this.commandList = commandList; } @Override public void execute() { // 批量执行 for (Command command : commandList) { command.execute(); } } }
3、应用
说明
- 命令对象一旦创建完成,随时可以调用。
- 可以在不同线程中调用命令对象,因此衍生一些应用场景。
- 工作队列
- 日程安排
- 线程池
示例:工作队列 + 命令模式
将待执行任务定义成 Command 对象,提供 execute() 方法。
- 添加任务:往队尾添加一个 Command 对象。
- 执行任务:存在空闲线程时,在队头重复执行操作。
- 取出一个 Command 对象,调用 execute() 方法。
- 等待调用完成,丢弃该命令对象。