一、定义
命令模式(Command Pattern)是一种行为型设计模式,它将一个请求封装为一个对象,从而使你可以用不同的请求、队列或者日志来参数化其他对象,也支持可撤销的操作。
换句话说,命令模式将“动作的调用者”和“动作的执行者”解耦,通过封装请求(命令对象)达到请求调用和执行分离。
二、适用场景
- 需要将请求调用者与请求接收者解耦,使调用者不必知道请求的具体执行细节;
- 需要支持对请求的队列、日志、撤销和重做操作;
- 需要将一组操作封装成一个命令,实现事务式的调用;
- 需要将操作参数化,支持动态组合和扩展。
三、核心角色
角色 |
说明 |
Command |
抽象命令,声明执行命令的接口 |
ConcreteCommand |
具体命令,实现抽象命令接口,绑定接收者和动作 |
Receiver |
接收者,负责具体执行命令相关的业务逻辑 |
Invoker |
调用者,负责命令的请求和调用 |
Client |
客户端,负责创建具体命令并设置接收者和调用者 |
四、UML 类图
+----------------+ +-----------------------+ +--------------+
| Client | | Invoker | | Receiver |
+----------------+ +-----------------------+ +--------------+
| | |
| 1. 创建命令 | |
|------------------------->| |
| | 2. 保存命令 |
| |----------------------------->|
| | |
| | 3. 调用命令执行 |
| |----------------------------->|
+----------------+ +-----------------------+ +--------------+
| Command |<-------->| ConcreteCommand | | |
+----------------+ +-----------------------+ +--------------+
| +execute() | | +execute() | | +action() |
+----------------+ +-----------------------+ +--------------+
五、真实业务场景示例:智能家居遥控器
假设你有一个智能家居系统,包含灯(Light)、空调(AirConditioner)等设备,需要实现遥控器控制设备的开关操作,且支持操作撤销。
1. 抽象命令接口(Command 角色)
/**
* 抽象命令接口,声明执行和撤销命令方法
*/
public interface Command {
void execute();
void undo();
}
2. 接收者类(Receiver 角色)
/**
* 灯,执行具体开关操作
*/
public class Light {
private String location;
public Light(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " 灯打开");
}
public void off() {
System.out.println(location + " 灯关闭");
}
}
/**
* 空调,执行具体开关操作
*/
public class AirConditioner {
public void on() {
System.out.println("空调打开");
}
public void off() {
System.out.println("空调关闭");
}
}
3. 具体命令类(ConcreteCommand 角色)
/**
* 灯的开命令
*/
public 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();
}
}
/**
* 灯的关命令
*/
public 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();
}
}
/**
* 空调开命令
*/
public class AirConditionerOnCommand implements Command {
private AirConditioner ac;
public AirConditionerOnCommand(AirConditioner ac) {
this.ac = ac;
}
@Override
public void execute() {
ac.on();
}
@Override
public void undo() {
ac.off();
}
}
/**
* 空调关命令
*/
public class AirConditionerOffCommand implements Command {
private AirConditioner ac;
public AirConditionerOffCommand(AirConditioner ac) {
this.ac = ac;
}
@Override
public void execute() {
ac.off();
}
@Override
public void undo() {
ac.on();
}
}
4. 调用者类(Invoker 角色)
/**
* 遥控器,调用命令执行和撤销
*/
public class RemoteControl {
private Command slot; // 这里简单用一个槽位模拟按键
private Command lastCommand; // 用于撤销操作
public void setCommand(Command command) {
this.slot = command;
}
public void pressButton() {
slot.execute();
lastCommand = slot;
}
public void pressUndo() {
if (lastCommand != null) {
lastCommand.undo();
}
}
}
5. 客户端代码(Client)
public class CommandPatternDemo {
public static void main(String[] args) {
// 创建接收者
Light livingRoomLight = new Light("客厅");
AirConditioner ac = new AirConditioner();
// 创建具体命令
Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);
Command acOn = new AirConditionerOnCommand(ac);
Command acOff = new AirConditionerOffCommand(ac);
// 创建调用者
RemoteControl remote = new RemoteControl();
// 控制灯开
remote.setCommand(lightOn);
remote.pressButton();
// 撤销灯开操作
remote.pressUndo();
// 控制空调开
remote.setCommand(acOn);
remote.pressButton();
// 控制灯关
remote.setCommand(lightOff);
remote.pressButton();
// 撤销灯关操作
remote.pressUndo();
}
}
六、类与角色对照表
类名 |
模式角色 |
说明 |
|
抽象命令 |
定义命令接口 |
|
具体命令 |
绑定灯的开操作 |
|
具体命令 |
绑定灯的关操作 |
|
具体命令 |
绑定空调开操作 |
|
具体命令 |
绑定空调关操作 |
|
接收者 |
执行灯的具体操作 |
|
接收者 |
执行空调的具体操作 |
|
调用者 |
调用命令,执行或撤销 |
|
客户端 |
组装命令和调用者 |
七、优缺点分析(Pros & Cons)
✅ 优点
优点 |
说明 |
解耦调用者和接收者 |
调用者只需要调用命令接口,不需要知道具体操作 |
易于扩展命令 |
新命令只需实现命令接口,无需改动调用者 |
支持撤销和恢复 |
命令可记录状态,实现撤销操作 |
支持宏命令 |
可以组合多个命令,支持批量执行 |
❌ 缺点
缺点 |
说明 |
命令类膨胀 |
命令类数量随着请求种类增长,可能变多 |
设计复杂度提升 |
增加了系统复杂度,学习和理解成本较高 |
八、真实业务应用场景
场景 |
说明 |
GUI 按钮操作 |
将按钮操作封装为命令,支持撤销重做 |
事务管理 |
将数据库操作封装为命令,实现事务回滚 |
远程调用 |
将请求封装为命令,支持异步和队列处理 |
宏命令 |
将多个命令组合成宏命令,批量执行 |
智能家居控制 |
设备操作封装成命令,遥控器调用,支持撤销 |
九、总结(Summary)
命令模式通过封装请求,实现调用者和执行者的解耦,使系统更加灵活和易于扩展。它不仅方便支持撤销、重做操作,也利于事务和队列处理,是行为型设计模式中非常实用的一种。