设计模式-命令模式
写在前面
Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!
一、什么是命令模式?
命令模式是行为模式中的一种,通过将请求封装成对象,使开发者可以用不同的请求、队列或日志来参数化其他对象。
类比订外卖的过程:用户(触发者)在APP下单(命令),餐厅收到订单后(接收者)开始制作。平台不需要关心具体由谁做饭,只负责传递订单。
二、命令模式解释
命令模式的核心架构:
-
命令(Command)
声明执行操作的统一接口,将请求封装为独立对象,使调用者与实现者解耦。 -
接收者(Receiver)
封装具体业务能力,仅关注领域逻辑实现,不感知调用流程。 -
具体命令(ConcreteCommand)
持有 Receiver 引用,在 Command 接口方法中通过委托执行 Receiver 的业务方法,可存储请求上下文参数。 -
触发者(Invoker)
维护命令执行入口(触发命令方法),支持命令的存储和调度(如实现命令队列或历史记录),仅依赖 Command 接口。 -
客户端(Client)
创建 Receiver 实例,构造 ConcreteCommand 并绑定 Receiver,配置 Invoker 与命令的关联关系。
三、案例实践
以居家控制系统为例,实现对灯光、空调的远程控制。先展示不使用命令模式的实现,再分析其不足。
基础类(接收者)
// 电灯类
public class Light {public void on() { System.out.println("开灯"); }public void off() { System.out.println("关灯"); }
}// 空调类
public class AirConditioner {public void on() { System.out.println("空调开启"); }public void off() { System.out.println("空调关闭"); }
}
硬编码实现
public class HomeController {private Light light;private AirConditioner ac;public HomeController() {light = new Light();ac = new AirConditioner();}// 每新增一个设备,就需要修改 switch-casepublic void executeCommand(String command) {switch (command) {case "LIGHT_ON": light.on(); break;case "AC_ON": ac.on(); break;default: System.out.println("未知命令");}}
}
问题分析:新增设备(如窗帘)时,必须修改 HomeController
的 switch-case
结构。随着功能增加,代码将难以维护,且难以实现组合操作。
命令模式实现
1. 抽象命令类
public interface Command {void execute(); // 执行命令(例如开灯)void undo(); // 撤销命令(例如关灯)
}
2. 具体命令
// 电灯命令
public class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) { this.light = light; }@Overridepublic void execute() { light.on(); }@Overridepublic void undo() { light.off(); }
}// 空调命令
public class ACOnCommand implements Command {private AirConditioner ac;public ACOnCommand(AirConditioner ac) { this.ac = ac; }@Overridepublic void execute() { ac.on(); }@Overridepublic void undo() { ac.off(); }
}
3. 控制器(触发者)
public class SmartHomeController {// 命令存储private Map<String, Command> commands = new HashMap<>();// 操作历史记录private Stack<Command> history = new Stack<>();// 绑定命令:例如将"开灯"按钮绑定到 LightOnCommandpublic void setCommand(String buttonName, Command command) {commands.put(buttonName, command);}// 执行命令public void executeCommand(String buttonName) {Command cmd = commands.get(buttonName);if (cmd != null) {cmd.execute();history.push(cmd); // 记录历史操作}}// 撤销上一步public void undoLastCommand() {if (!history.isEmpty()) {Command lastCmd = history.pop();lastCmd.undo();}}
}
4. 测试类(客户端)
@Test
public void test() {Light light = new Light();AirConditioner ac = new AirConditioner();SmartHomeController controller = new SmartHomeController();// 绑定命令controller.setCommand("开灯", new LightOnCommand(light));controller.setCommand("开空调", new ACOnCommand(ac));// 执行操作controller.executeCommand("开灯"); // 输出:开灯controller.executeCommand("开空调"); // 输出:空调开启controller.undoLastCommand(); // 输出:空调关闭
}
四、如何新增设备?以窗帘为例
1. 新增窗帘类(接收者)
public class Curtain {public void open() { System.out.println("打开窗帘"); }public void close() { System.out.println("关闭窗帘"); }
}
2. 新增窗帘命令
public class CurtainCommand implements Command {private Curtain curtain;public CurtainCommand(Curtain curtain) { this.curtain = curtain; }@Overridepublic void execute() { curtain.close(); } // 执行:关闭窗帘@Overridepublic void undo() { curtain.open(); } // 撤销:打开窗帘
}
3. 使用新功能(无需修改控制器)
// 绑定新命令
Curtain curtain = new Curtain();
controller.setCommand("关窗帘", new CurtainCommand(curtain));
controller.executeCommand("关窗帘"); // 输出:关闭窗帘
改进效果:新增设备时,只需添加对应的 Command 类,无需修改控制器代码,且更容易实现组合命令。
五、长话短说
核心思想
将操作抽象为独立对象,通过参数化方式传递请求,解耦调用者与实现者。
适用场景
-
需要解耦请求发送者与执行者
-
需支持撤销/重做操作
-
需实现事务操作(组合命令)
-
需记录操作历史
-
需支持任务队列或线程池调度
代码实现步骤
-
定义 Command 接口,声明执行方法。
-
创建具体命令类实现接口,并注入 Receiver 对象。
-
创建 Invoker 类管理命令对象。
-
客户端初始化顺序:
a. 创建 Receiver
b. 创建 Command 并关联 Receiver
c. 创建 Invoker 并绑定 Command -
通过 Invoker 执行命令。
典型应用
-
智能家居控制系统
-
文本编辑器的撤销/重做
-
事务型数据库操作
-
任务调度系统
-
GUI 事件处理