文章目录
1. 什么是命令模式?
命令模式是一种行为型设计模式,它将请求(命令)封装为一个对象,从而使你可以使用不同的请求参数化客户端,队列或记录请求日志,以及支持可撤销的操作。
简单来说,命令模式就是将"请求"转化为一个对象,这个对象可以被存储、传递、调用,而且可以在不同的时间点被请求调用,即使发送请求的对象已经不存在。
2. 为什么需要命令模式?
在以下情况下,命令模式特别有用:
- 需要参数化操作:当你需要根据运行时确定的请求参数来执行操作时
- 需要将操作放入队列:当操作需要排队执行,或者在不同时间执行时
- 需要支持撤销/重做功能:当系统需要支持操作的撤销和重做功能时
- 需要支持事务:当操作需要作为一个事务执行,要么全部完成,要么全部不做
- 需要将发送者与接收者解耦:当请求发送者不需要知道请求如何被处理以及由谁处理时
3. 命令模式的核心概念
命令模式涉及以下几个核心角色:
-
命令(Command):
- 声明执行操作的接口
- 通常只有一个执行方法(如
execute()
)
-
具体命令(Concrete Command):
- 实现命令接口
- 通常持有接收者的引用
- 调用接收者的相关操作来完成命令的执行
-
接收者(Receiver):
- 知道如何实施与命令相关的操作
- 任何类都可以作为接收者
-
调用者(Invoker):
- 要求命令对象执行请求
- 不知道命令是如何执行的,也不知道具体的接收者是谁
-
客户端(Client):
- 创建具体的命令对象并设置它的接收者
- 将命令对象交给调用者
4. 命令模式的结构
命令模式的UML类图如下:
+----------------+ +----------------+
| Invoker |------>| Command |
+----------------+ +----------------+
| execute() |
+----------------+
↑
|
+----------------+
|ConcreteCommand |
+----------------+
| execute() |
+----------------+
|
|
v
+----------------+
| Receiver |
+----------------+
| action() |
+----------------+
5. 命令模式的基本实现
5.1 简单的灯光控制示例
下面是一个简单的灯光控制示例,展示了命令模式的基本实现。
首先,定义命令接口:
// 命令接口
public interface Command {
void execute();
}
然后,定义接收者类(灯):
// 接收者类
public class Light {
private String location;
public Light(String location) {
this.location = location;
}
public void turnOn() {
System.out.println(location + " 灯已打开");
}
public void turnOff() {
System.out.println(location + " 灯已关闭");
}
}
接着,定义具体命令类:
// 打开灯的命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
}
// 关闭灯的命令
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
}
然后,定义调用者类(远程控制器):
// 调用者类
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
最后,客户端代码:
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建接收者
Light livingRoomLight = new Light("客厅");
Light kitchenLight = new Light("厨房");
// 创建具体命令
Command livingRoomLightOn = new LightOnCommand(livingRoomLight);
Command livingRoomLightOff = new LightOffCommand(livingRoomLight);
Command kitchenLightOn = new LightOnCommand(kitchenLight);
Command kitchenLightOff = new LightOffCommand(kitchenLight);
// 创建调用者
RemoteControl remote = new RemoteControl();
// 使用远程控制器打开客厅灯
remote.setCommand(livingRoomLightOn);
remote.pressButton();
// 使用远程控制器关闭客厅灯
remote.setCommand(livingRoomLightOff);
remote.pressButton();
// 使用远程控制器打开厨房灯
remote.setCommand(kitchenLightOn);
remote.pressButton();
// 使用远程控制器关闭厨房灯
remote.setCommand(kitchenLightOff);
remote.pressButton();
}
}
输出结果:
客厅 灯已打开
客厅 灯已关闭
厨房 灯已打开
厨房 灯已关闭
5.2 家电控制示例
我们可以扩展上面的例子,添加更多种类的家电控制:
// 音响系统接收者
public class StereoSystem {
private String location;
public StereoSystem(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " 音响已打开");
}
public void off() {
System.out.println(location + " 音响已关闭");
}
public void setCD() {
System.out.println(location + " 音响已设置为CD播放模式");
}
public void setVolume(int volume) {
System.out.println(location + " 音响音量已设置为 " + volume);
}
}
// 电风扇接收者
public class Fan {
private String location;
public Fan(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " 电风扇已打开");
}
public void off() {
System.out.println(location + " 电风扇已关闭");
}
public void setHigh() {
System.out.println(location + " 电风扇已设置为高速");
}
public void setMedium() {
System.out.println(location + " 电风扇已设置为中速");
}
public void setLow() {
System.out.println(location + " 电风扇已设置为低速");
}
}
然后,为这些家电创建对应的命令:
// 打开音响的命令
public class StereoOnWithCDCommand implements Command {
private StereoSystem stereo;
public StereoOnWithCDCommand(StereoSystem stereo) {
this.stereo = stereo;
}
@Override
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}
// 关闭音响的命令
public class StereoOffCommand implements Command {
private StereoSystem stereo;
public StereoOffCommand(StereoSystem stereo) {
this.stereo = stereo;
}
@Override
public void execute() {
stereo.off();
}
}
// 打开风扇并设置高速的命令
public class FanHighCommand implements Command {
private Fan fan;
public FanHighCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.on();
fan.setHigh();
}
}
// 关闭风扇的命令
public class FanOffCommand implements Command {
private Fan fan;
public FanOffCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.off();
}
}
修改调用者类,支持多个按钮:
// 多按钮遥控器
public class MultiButtonRemote {
private Command[] onCommands;
private Command[] offCommands;
public MultiButtonRemote(int slotCount) {
onCommands = new Command[slotCount];
offCommands = new Command[slotCount];
// 初始化所有命令为空命令对象,避免空指针异常
Command noCommand = new NoCommand(); // NoCommand是一个空实现
for (int i = 0; i < slotCount; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void pressOnButton(int slot) {
onCommands[slot].execute();
}
public void pressOffButton(int slot) {
offCommands[slot].execute();
}
}
// 空命令 - 用于初始化,避免空指针异常
public class NoCommand implements Command {
@Override
public void execute() {
// 什么也不做
}
}
客户端代码:
public class HomeAutomationDemo {
public static void main(String[] args) {
// 创建接收者
Light livingRoomLight = new Light("客厅");
Light kitchenLight = new Light("厨房");
StereoSystem stereo = new StereoSystem("客厅");
Fan ceilingFan = new Fan("卧室");
// 创建具体命令
Command livingRoomLightOn = new LightOnCommand(livingRoomLight);
Command livingRoomLightOff = new LightOffCommand(livingRoomLight);
Command kitchenLightOn = new LightOnCommand(kitchenLight);
Command kitchenLightOff = new LightOffCommand(kitchenLight);
Command stereoOnWithCD = new StereoOnWithCDCommand(stereo);
Command stereoOff = new StereoOffCommand(stereo);
Command fanHigh = new FanHighCommand(ceilingFan);
Command fanOff = new FanOffCommand(ceilingFan);
// 创建多按钮遥控器
MultiButtonRemote remote = new MultiButtonRemote(4);
// 设置每个插槽对应的命令
remote.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remote.setCommand(1, kitchenLightOn, kitchenLightOff);
remote.setCommand(2, stereoOnWithCD, stereoOff);
remote.setCommand(3, fanHigh, fanOff);
// 测试按钮
System.out.println("------ 按下第1个按钮的开按钮 --