一、核心定义与设计思想
命令模式通过对象化请求,将操作的具体实现细节封装在命令对象中,使得调用者(Invoker)无需直接依赖接收者(Receiver),仅需通过命令对象间接调用。这种设计支持以下能力:
- 解耦请求发送者与执行者:调用者仅与命令接口交互,无需了解接收者的实现细节。
- 支持扩展性与灵活性:新增命令只需实现接口,符合开闭原则。
- 实现高级功能:如撤销(Undo)、重做(Redo)、事务管理及日志记录。
二、模式结构与角色
命令模式包含以下核心角色:
- Command(抽象命令接口)
声明执行操作的统一接口,通常包含execute()
方法,部分场景扩展undo()
方法以支持撤销。public interface Command { void execute(); void undo(); // 可选 }
- ConcreteCommand(具体命令类)
实现命令接口,持有接收者的引用并调用其方法。例如:public class LightOnCommand implements Command { private Light receiver; public LightOnCommand(Light receiver) { this.receiver = receiver; } public void execute() { receiver.turnOn(); } }
- Receiver(接收者)
实际执行操作的对象,例如电灯、文本编辑器等业务逻辑实现类。 - Invoker(调用者)
触发命令执行的入口,不关心命令具体实现。例如遥控器或按钮:public class RemoteControl { private Command command; public void pressButton() { command.execute(); } }
- Client(客户端)
创建具体命令对象并绑定接收者,将其传递给调用者。
三、代码示例与实现
场景:遥控器控制家电(经典案例)
- 定义接收者
class Light { public void turnOn() { System.out.println("灯已开启"); } public void turnOff() { System.out.println("灯已关闭"); } }
- 实现具体命令
class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.turnOn(); } }
- 调用者与客户端交互
结果:调用者与接收者完全解耦,支持灵活替换命令。public class Client { public static void main(String[] args) { Light light = new Light(); Command cmdOn = new LightOnCommand(light); RemoteControl control = new RemoteControl(); control.setCommand(cmdOn); control.pressButton(); // 输出:灯已开启 } }
四、高级应用场景
- 撤销与重做(Undo/Redo)
通过维护命令历史栈实现操作回滚。例如文本编辑器的撤销功能:class Editor { private String text; public void insert(String content) { /* 实现 */ } public void delete(String content) { /* 实现 */ } } class InsertCommand implements Command { private Editor receiver; private String content; public void execute() { receiver.insert(content); } public void undo() { receiver.delete(content); } }
- 宏命令(Macro Command)
将多个命令组合为一个复合命令,实现批量操作:class MacroCommand implements Command { private List commands = new ArrayList<>(); public void add(Command cmd) { commands.add(cmd); } public void execute() { commands.forEach(Command::execute); } }
- 异步任务队列
结合线程池处理异步请求,提升系统吞吐量:class CommandQueue { private BlockingQueue queue = new LinkedBlockingQueue<>(); public void addCommand(Command cmd) { queue.put(cmd); } public void process() { queue.forEach(cmd -> new Thread(cmd::execute).start()); } }
五、优缺点分析
优点 | 缺点 |
---|---|
解耦请求发送者与接收者,提升系统灵活性 | 可能导致类爆炸问题(每个操作需独立命令类) |
支持撤销、重做及事务管理,增强用户体验 | 增加系统复杂度,需额外维护命令对象 |
符合开闭原则,扩展性强 | 过度设计风险,简单场景可能不必要 |
六、适用场景
- GUI事件处理:如按钮点击、菜单操作。
- 事务性操作:数据库事务的提交与回滚。
- 日志与审计:记录操作历史以便恢复。
- 异步任务调度:如消息队列中的任务处理。
七、与其他模式的对比
- 策略模式:关注算法替换,而命令模式关注请求封装。
- 观察者模式:通过订阅-发布解耦,命令模式通过对象化请求解耦。
- 责任链模式:请求逐级传递,命令模式明确单一行执行路径。
总结
命令模式通过对象化请求实现了高度解耦与灵活控制,是构建可扩展系统的利器。其核心价值在于将操作抽象为对象,支持复杂功能(如撤销、异步队列)的同时降低系统耦合度。实际应用中需根据场景权衡复杂性,避免滥用。