命令模式:把请求变成可以保存和撤销的对象
点击按钮、操作队列、撤销重做、任务调度,都要求“动作”本身能像数据一样被保存和传递。命令模式把动作对象化。
一、痛点:调用者直接绑死接收者
一个智能家居系统里,用户可能通过面板、语音、定时任务三种方式执行“开灯”“关灯”“调节温度”。如果每种入口都直接调用设备方法:
if ("light-on".equals(action)) {
light.turnOn();
} else if ("ac-up".equals(action)) {
ac.increaseTemperature();
}
调用者和设备实现强耦合,也没法支持撤销、排队、日志回放。
命令模式的回答:把每个动作封装成 Command 对象,调用者只向命令对象发出“执行”,不关心谁真正干活。
二、实现:命令接口 → 具体命令 → 调用者 → 接收者
第一步:定义命令接口
public interface Command {
void execute();
void undo();
}
第二步:定义接收者
public class Light {
private boolean on;
public void turnOn() {
this.on = true;
System.out.println("[灯] 已打开");
}
public void turnOff() {
this.on = false;
System.out.println("[灯] 已关闭");
}
}
第三步:实现具体命令
public class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOn();
}
@Override
public void undo() {
light.turnOff();
}
}
public class LightOffCommand implements Command {
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.turnOff();
}
@Override
public void undo() {
light.turnOn();
}
}
第四步:调用者与历史记录
import java.util.ArrayDeque;
import java.util.Deque;
public class RemoteController {
private final Deque<Command> history = new ArrayDeque<>();
public void press(Command command) {
command.execute();
history.push(command);
}
public void undo() {
if (!history.isEmpty()) {
history.pop().undo();
}
}
}
第五步:不同入口复用同一套命令
Light light = new Light();
RemoteController controller = new RemoteController();
controller.press(new LightOnCommand(light));
controller.press(new LightOffCommand(light));
controller.undo(); // 重新开灯
面板、语音、定时任务都只是创建同一个 LightOnCommand 的入口,设备逻辑只存在一处。
三、命令模式解决的四类问题
| 问题 | 命令模式的解法 |
|---|---|
| 调用者与执行者耦合 | 调用者只依赖 Command 接口 |
| 需要撤销/重做 | 命令对象保存历史,并可执行 undo |
| 需要延迟执行或定时执行 | 命令对象可以被排队、持久化、定时触发 |
| 需要记录操作日志 | 每个命令天然是日志条目 |
四、工程化提醒
- 只有动作需要被管理时才引入命令模式:普通业务方法直接调用即可,别把所有调用都包装成 Command;
- 命令粒度要合适:一个命令最好对应一个可撤销的业务动作,太细会类爆炸,太粗会难回滚;
- 与真实队列配合:生产上可将 Command 序列化到消息队列,实现任务解耦和重试。
小结
命令模式一句话:把动作封装成对象,让动作可保存、可排队、可撤销。它解耦调用者与接收者,也为复杂操作管理提供了统一入口。

404

被折叠的 条评论
为什么被折叠?



