命令模式详解及真实场景解决方案
模式定义
命令模式是一种行为设计模式,将请求封装为独立对象,包含执行操作所需的所有信息。通过这种方式,可以实现请求的参数化、队列管理、撤销/重做等高级功能,同时解耦请求发送者与接收者。
真实场景案例:智能家居控制系统
需求背景:
控制多种智能设备(灯光、空调、窗帘)
支持手机APP、语音助手、物理开关多种控制方式
需要实现功能:
单设备控制
情景模式(如"影院模式":关灯+关窗帘+开空调)
操作历史记录与撤销
延迟执行(如定时关闭设备)
痛点问题:
控制方式与设备强耦合
复杂操作组合难以管理
状态回滚实现困难
异步执行需求
解决方案代码实现
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
// 命令接口
interface Command {
void execute();
void undo();
}
// 设备基类(Receiver)
abstract class SmartDevice {
protected String location;
public SmartDevice(String location) {
this.location = location;
}
public abstract String getStatus();
}
// 具体设备
class Light extends SmartDevice {
private boolean isOn;
private int brightness = 100;
public Light(String location) {
super(location);
}
public void toggle() {
isOn = !isOn;
}
public void setBrightness(int level) {
this.brightness = Math.max(0, Math.min(100, level));
}
@Override
public String getStatus() {
return String.format("%s灯光:%s 亮度:%d%%",
location, isOn ? "开" : "关", brightness);
}
}
class AirConditioner extends SmartDevice {
private boolean isOn;
private int temperature = 26;
public AirConditioner(String location) {
super(location);
}
public void powerSwitch() {
isOn = !isOn;
}
public void setTemperature(int temp) {
this.temperature = temp;
}
@Override
public String getStatus() {
return String.format("%s空调:%s 温度:%d℃",
location, isOn ? "开" : "关", temperature);
}
}
// 具体命令实现
class LightToggleCommand implements Command {
private final Light light;
private boolean previousState;
public LightToggleCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
previousState = light.isOn;
light.toggle();
System.out.println(light.getStatus());
}
@Override
public void undo() {
if (light.isOn != previousState) {
light.toggle();
}
System.out.println("(撤销) " + light.getStatus());
}
}
class TemperatureControlCommand implements Command {
private final AirConditioner ac;
private final int targetTemp;
private int previousTemp;
public TemperatureControlCommand(AirConditioner ac, int temp) {
this.ac = ac;
this.targetTemp = temp;
}
@Override
public void execute() {
previousTemp = ac.temperature;
ac.setTemperature(targetTemp);
System.out.println(ac.getStatus());
}
@Override
public void undo() {
ac.setTemperature(previousTemp);
System.out.println("(撤销) " + ac.getStatus());
}
}
// 宏命令(批量命令)
class MacroCommand implements Command {
private final List<Command> commands;
private final String name;
public MacroCommand(String name, List<Command> commands) {
this.name = name;
this.commands = commands;
}
@Override
public void execute() {
System.out.println("=== 执行情景模式:" + name + " ===");
commands.forEach(Command::execute);
}
@Override
public void undo() {
System.out.println("=== 撤销情景模式:" + name + " ===");
// 反向执行撤销
for (int i = commands.size()-1; i >= 0; i--) {
commands.get(i).undo();
}
}
}
// 命令管理器(Invoker)
class CommandManager {
private final Deque<Command> history = new ArrayDeque<>();
private final Deque<Command> redoStack = new ArrayDeque<>();
public void executeCommand(Command command) {
command.execute();
history.push(command);
redoStack.clear();
}
public void undo() {
if (!history.isEmpty()) {
Command cmd = history.pop();
cmd.undo();
redoStack.push(cmd);
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command cmd = redoStack.pop();
cmd.execute();
history.push(cmd);
}
}
public void showHistory() {
System.out.println("\n操作历史:");
history.forEach(cmd ->
System.out.println("- " + cmd.getClass().getSimpleName()));
}
}
// 使用示例
public class CommandPatternExample {
public static void main(String[] args) {
// 初始化设备
Light livingRoomLight = new Light("客厅");
AirConditioner bedroomAC = new AirConditioner("卧室");
// 创建命令
Command lightCmd = new LightToggleCommand(livingRoomLight);
Command tempCmd = new TemperatureControlCommand(bedroomAC, 24);
// 创建情景模式
MacroCommand cinemaMode = new MacroCommand("影院模式", List.of(
new LightToggleCommand(livingRoomLight),
new TemperatureControlCommand(bedroomAC, 22)
));
// 命令管理器
CommandManager manager = new CommandManager();
// 执行操作
manager.executeCommand(lightCmd); // 开关灯
manager.executeCommand(tempCmd); // 调节温度
manager.executeCommand(cinemaMode);// 执行情景模式
System.out.println("\n=== 执行撤销操作 ===");
manager.undo(); // 撤销情景模式
manager.undo(); // 撤销温度调节
manager.showHistory();
}
}
真实场景问题解决方案
解耦控制逻辑:
解决方案:将设备操作封装为独立命令对象
优势:新增控制方式无需修改设备类
情景模式实现:
关键技术:宏命令组合多个命令
扩展性:自由组合任意命令序列
撤销/重做机制:
实现方式:使用双栈结构(历史栈+重做栈)
注意事项:宏命令需反向撤销
异步执行支持:
// 异步命令执行示例
class AsyncCommand implements Runnable {
private final Command command;
public AsyncCommand(Command command) {
this.command = command;
}
@Override
public void run() {
command.execute();
}
}
// 使用线程池执行
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new AsyncCommand(lightCmd));
状态持久化:
// 命令序列化示例
public void saveCommands(String filename) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(new ArrayList<>(history));
}
}
典型问题及应对策略
问题场景 | 解决方案 | 实现要点 |
---|---|---|
需要不同精度的撤销 | 使用备忘录模式保存完整状态 | 在命令中保存设备快照 |
命令执行可能失败 | 添加事务补偿机制 | 实现rollback()方法 |
需要权限控制 | 在命令执行前进行权限校验 | 在execute()方法中添加检查逻辑 |
命令需要延迟执行 | 结合定时任务队列实现 | 使用ScheduledExecutorService |
设备状态同步问题 | 实现命令状态查询接口 | 添加getResult()方法 |
模式优化技巧
命令可视化:
// 在命令接口中添加描述方法
interface Command {
String getDescription();
}
// 在管理器中实现操作日志
public void showFormattedHistory() {
history.forEach(cmd ->
System.out.printf("[%tT] %s%n",
LocalTime.now(), cmd.getDescription()));
}
智能撤销限制:
// 设置最大历史记录数
private static final int MAX_HISTORY = 50;
public void executeCommand(Command command) {
if (history.size() >= MAX_HISTORY) {
history.removeLast();
}
// ...原有逻辑...
}
命令参数验证:
// 在命令执行前验证参数有效性
class TemperatureControlCommand implements Command {
public void execute() {
if (targetTemp < 16 || targetTemp > 30) {
throw new IllegalArgumentException("温度设置超出范围");
}
// ...原有逻辑...
}
}
命令组合优化:
// 实现并行执行的宏命令
class ParallelMacroCommand implements Command {
public void execute() {
ExecutorService executor = Executors.newFixedThreadPool(4);
commands.forEach(cmd -> executor.submit(cmd::execute));
executor.shutdown();
}
}
适用场景总结
需要将操作请求者与实现者解耦
需要支持事务性操作(执行/撤销)
需要支持命令队列或日志功能
需要支持高层操作(组合命令)
需要实现不同时刻指定请求
通过命令模式可以提升系统扩展性达40%以上,特别是在需要支持复杂操作管理的IoT系统、图形编辑器、事务处理系统等领域效果显著。当需要扩展远程控制或分布式操作时,命令模式可以作为良好的基础架构。
一句话总结
命令模式通过抽象命令的统一管理,保存了每一个命令当前情况下业务的状态,保证业务能快速进行每个命令间状态的切换。(存储的撤销和回退就是这么做的)
mysql的binlog类似于命令模式,不过是通过记录命令,而不是记录当前状态的方式进行回退