命令模式
命令模式(Command Pattern)是一种行为型设计模式,核心思想是将请求封装为独立对象,实现请求的发起者与执行者的解耦,并支持请求的队列化、撤销、重做、日志记录等功能。通俗来说,它像餐厅的“订单系统”——顾客(调用者)通过服务员(命令对象)传递订单,厨师(接收者)根据订单烹饪菜品,整个过程无需顾客与厨师直接交互。
一、通俗理解
以智能家居控制为例:
- 传统方式:遥控器直接调用电灯的
turnOn()
方法,导致遥控器和电灯强耦合,新增功能需修改遥控器代码。 - 命令模式:
- 遥控器(调用者)持有命令对象(如“开灯命令”)。
- 命令对象封装电灯操作,调用时触发电灯(接收者)的对应方法。
此时,遥控器无需知道电灯如何实现开关,只需操作命令对象,支持灵活扩展新设备。
二、模式结构
命令模式包含以下角色:
- 命令接口(Command):定义执行(
execute()
)和撤销(undo()
)方法。 - 具体命令(ConcreteCommand):绑定接收者与操作(如
LightOnCommand
调用电灯的turnOn()
)。 - 接收者(Receiver):实际执行操作的对象(如电灯、文件系统)。
- 调用者(Invoker):触发命令执行(如遥控器、GUI按钮)。
- 客户端(Client):组装命令对象与接收者。
三、适用场景
- GUI操作:按钮点击事件、菜单命令。
- 事务管理:支持操作回滚(如数据库事务)。
- 日志记录:记录命令执行历史用于审计。
- 异步任务队列:将命令存入队列延迟执行。
四、代码实现
1. C++ 示例(智能家居控制)
#include <iostream>
#include <stack> // 接收者:电灯
class Light {
public: void on() { std::cout << "电灯已开启\n"; } void off() { std::cout << "电灯已关闭\n"; }
}; // 命令接口
class Command {
public: virtual void execute() = 0; virtual void undo() = 0; virtual ~Command() = default;
}; // 具体命令:开灯
class LightOnCommand : public Command { Light& light;
public: LightOnCommand(Light& l) : light(l) {} void execute() override { light.on(); } void undo() override { light.off(); }
}; // 调用者:遥控器
class RemoteControl { std::stack<Command*> history;
public: void pressButton(Command* cmd) { cmd->execute(); history.push(cmd); } void pressUndo() { if (!history.empty()) { history.top()->undo(); history.pop(); } }
}; // 客户端
int main() { Light light; LightOnCommand cmd(light); RemoteControl remote; remote.pressButton(&cmd); // 输出:电灯已开启 remote.pressUndo(); // 输出:电灯已关闭 return 0;
}
2. Python 示例(文本编辑器撤销功能)
from abc import ABC, abstractmethod # 命令接口
class Command(ABC): @abstractmethod def execute(self): pass @abstractmethod def undo(self): pass # 接收者:文档
class Document: def __init__(self): self.content = "" def write(self, text): self.content += text def erase_last(self): self.content = self.content[:-1] # 具体命令:输入文本
class TextCommand(Command): def __init__(self, doc, text): self.doc = doc self.text = text def execute(self): self.doc.write(self.text) def undo(self): self.doc.erase_last() # 调用者:编辑器
class Editor: def __init__(self): self.history = [] def type_text(self, command): command.execute() self.history.append(command) def undo(self): if self.history: cmd = self.history.pop() cmd.undo() # 客户端
doc = Document()
editor = Editor()
cmd = TextCommand(doc, "Hello") editor.type_text(cmd)
print(doc.content) # 输出:Hello
editor.undo()
print(doc.content) # 输出:Hell
3. Java 示例(支持宏命令)
import java.util.*; // 命令接口
interface Command { void execute(); void undo();
} // 接收者:音响
class Speaker { public void playMusic() { System.out.println("播放音乐"); } public void stopMusic() { System.out.println("停止音乐"); }
} // 具体命令:播放音乐
class PlayCommand implements Command { private Speaker speaker; public PlayCommand(Speaker s) { this.speaker = s; } public void execute() { speaker.playMusic(); } public void undo() { speaker.stopMusic(); }
} // 宏命令(组合多个命令)
class MacroCommand implements Command { private List<Command> commands = new ArrayList<>(); public void add(Command cmd) { commands.add(cmd); } public void execute() { commands.forEach(Command::execute); } public void undo() { Collections.reverse(commands); commands.forEach(Command::undo); }
} // 客户端
public class Client { public static void main(String[] args) { Speaker speaker = new Speaker(); MacroCommand macro = new MacroCommand(); macro.add(new PlayCommand(speaker)); macro.add(new PlayCommand(speaker)); macro.execute(); // 输出两次“播放音乐” macro.undo(); // 输出两次“停止音乐” }
}
五、优缺点分析
优点 | 缺点 |
---|---|
1. 解耦调用者与接收者:新增设备无需修改调用者代码 | 1. 类数量增加:每个命令需单独类,可能导致类爆炸 |
2. 支持撤销/重做:通过历史栈记录操作 | 2. 性能开销:频繁创建命令对象可能影响性能 |
3. 灵活扩展功能:如日志、队列、事务 | 3. 设计复杂度高:需合理拆分命令粒度 |
六、总结
命令模式通过封装请求为对象,实现了调用链路的灵活控制,尤其适用于需要动态管理操作历史的场景(如编辑器、智能家居)。其核心价值在于:
- 解耦思维:分离请求发起与执行,提升系统扩展性。
- 功能增强:通过命令队列、日志、宏命令等扩展功能。
- 实际应用:Java的
Runnable
、Spring的CommandLineRunner
均基于此模式。
扩展对比:
- 与策略模式:策略模式关注算法替换,命令模式关注操作封装与生命周期管理。
- 与观察者模式:观察者模式用于事件通知,命令模式用于操作封装。