文章目录
命令模式 (Command Pattern)
命令模式 是一种 行为型设计模式,用于将请求(命令)封装成对象,从而使得用户可以通过不同的请求来执行不同的操作。这使得请求发送者与接收者解耦,允许通过不同的方式来控制请求的执行,比如队列、日志、撤销、恢复等。
原理
-
核心思想:
- 将请求封装为一个对象,通过对象传递请求,以解耦发送请求的对象和执行请求的对象。
- 每个命令都将一个特定的操作封装成一个对象,发送者不需要知道命令如何执行,只需要调用命令的执行方法。
-
参与角色:
- Command(命令接口) :
- 定义执行命令的接口。
- ConcreteCommand(具体命令) :
- 实现命令接口,调用接收者的相应操作。
- Invoker(调用者) :
- 负责调用命令对象来执行请求,通常是客户端发起请求的地方。
- Receiver(接收者) :
- 执行与请求相关的实际操作,具体的业务逻辑由接收者来实现。
- Client(客户端) :
- 创建具体命令对象,并设置接收者。
- Invoker :
- 向命令对象发送请求。
- Command(命令接口) :
优点
- 解耦请求发送者与接收者 :
- 发送请求的对象与执行请求的对象之间没有直接依赖关系。
- 支持撤销操作 :
- 通过命令对象,可以很方便地支持撤销和恢复操作。
- 可以组合命令 :
- 命令模式支持宏命令,可以将多个命令组合成一个更复杂的命令对象。
- 扩展性强 :
- 可以增加新的命令,而不需要修改客户端代码,符合开闭原则。
缺点
- 增加类的数量 :
- 每个命令都需要创建一个类,可能导致类的数量增加。
- 复杂度提高 :
- 对于一些简单的请求,使用命令模式可能会导致设计过于复杂。
示例代码
场景描述
假设我们有一个遥控器可以控制家庭设备,如灯、风扇。我们希望通过命令模式来解耦遥控器与设备的操作。
1. 定义命令接口
java
// 命令接口
public interface Command {
void execute();
}
2. 定义具体命令类(实现命令接口)
java
// 打开灯的命令
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 FanOnCommand implements Command {
private Fan fan;
public FanOnCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.turnOn();
}
}
// 关闭风扇的命令
public class FanOffCommand implements Command {
private Fan fan;
public FanOffCommand(Fan fan) {
this.fan = fan;
}
@Override
public void execute() {
fan.turnOff();
}
}
3. 定义接收者类(设备)
java
// 灯类
public class Light {
public void turnOn() {
System.out.println("The light is ON");
}
public void turnOff() {
System.out.println("The light is OFF");
}
}
// 风扇类
public class Fan {
public void turnOn() {
System.out.println("The fan is ON");
}
public void turnOff() {
System.out.println("The fan is OFF");
}
}
4. 定义遥控器(调用者)
java
// 遥控器类
public class RemoteControl {
private Command slot;
public void setCommand(Command command) {
slot = command;
}
public void pressButton() {
slot.execute();
}
}
5. 客户端代码
java
public class CommandPatternExample {
public static void main(String[] args) {
// 创建设备
Light light = new Light();
Fan fan = new Fan();
// 创建命令
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
Command fanOn = new FanOnCommand(fan);
Command fanOff = new FanOffCommand(fan);
// 创建遥控器并设置命令
RemoteControl remote = new RemoteControl();
// 按下按钮打开灯
remote.setCommand(lightOn);
remote.pressButton();
// 按下按钮关闭灯
remote.setCommand(lightOff);
remote.pressButton();
// 按下按钮开启风扇
remote.setCommand(fanOn);
remote.pressButton();
// 按下按钮关闭风扇
remote.setCommand(fanOff);
remote.pressButton();
}
}
输出结果
text
The light is ON
The light is OFF
The fan is ON
The fan is OFF
UML 类图
+------------------+
| Command |
+------------------+
| + execute() |
+------------------+
^
|
+---------------+ +---------------+ +----------------+
| LightOnCommand| | LightOffCommand| | FanOnCommand |
+---------------+ +----------------+ +----------------+
| - light: Light| | - light: Light | | - fan: Fan |
| + execute() | | + execute() | | + execute() |
+---------------+ +----------------+ +----------------+
^
|
+------------------+ +------------------+
| Light | | Fan |
+------------------+ +------------------+
| + turnOn() | | + turnOn() |
| + turnOff() | | + turnOff() |
+------------------+ +------------------+
^
|
+--------------+
| RemoteControl|
+--------------+
| - slot: Command|
| + setCommand() |
| + pressButton()|
+--------------+
使用场景
- UI界面按钮处理 :
- 在图形界面中,可以通过按钮触发命令执行,例如开关按钮、调节音量、播放视频等。
- 任务调度 :
- 在任务调度系统中,可以将任务封装成命令对象并进行调度,支持操作的撤销和恢复。
- 菜单操作 :
- 在菜单系统中,每个菜单项都可以被封装为一个命令,通过命令对象来执行不同的功能。
- 事务管理 :
- 将所有操作封装成命令对象,支持事务的提交和回滚。
扩展与优化
-
命令组合:
- 可以将多个命令对象组合成一个宏命令,实现批量操作。例如,可以创建一个
MacroCommand
类,将多个命令对象组合在一起一次性执行。
- 可以将多个命令对象组合成一个宏命令,实现批量操作。例如,可以创建一个
-
支持撤销操作:
- 可以扩展命令接口,加入
undo()
方法,实现操作的撤销功能。每个具体命令类可以实现撤销操作,允许用户恢复到之前的状态。
- 可以扩展命令接口,加入
-
命令历史:
- 通过维护一个命令历史列表,可以实现命令的撤销与恢复,或者将命令队列保存为日志,支持后期回放。
小结
- 命令模式通过将请求封装成对象,允许在不同的时间和环境中进行请求的传递和执行。
- 解耦了请求的发送者与接收者,使得系统更加灵活且易于扩展。
- 适用于需要对请求进行队列、撤销、恢复、日志等操作的场景。