1. 从生活场景理解命令模式
每天早晨叫醒我的智能家居系统,其实就是个典型命令模式应用。当我对手机说"打开窗帘、烧热水、播放新闻"时,每个语音指令都被封装成独立命令对象。这种将请求封装为独立对象的设计理念,就是命令模式(Command Pattern)的核心思想。
在软件开发中,命令模式允许我们将"请求发送者"与"请求接收者"解耦。就像餐厅里顾客、服务员和后厨的关系:顾客(发送者)把订单(命令)交给服务员(调用者),服务员不需要知道具体如何烹饪,只需将订单传递给厨师(接收者)。
2. 基础实现示例
// 命令接口:烹饪命令基类
interface CookingCommand {
void execute();
}
// 具体命令:炒菜命令
class StirFryCommand implements CookingCommand {
private Chef receiver;
public StirFryCommand(Chef chef) {
this.receiver = chef;
}
@Override
public void execute() {
receiver.cookStirFry();
}
}
// 接收者:厨师类
class Chef {
public void cookStirFry() {
System.out.println("大厨正在爆炒青菜...");
System.out.println("加入秘制酱料翻炒");
}
}
// 调用者:服务员类
class Waiter {
private CookingCommand command;
public void takeOrder(CookingCommand cmd) {
this.command = cmd;
}
public void submitOrder() {
if (command != null) {
command.execute();
}
}
}
// 客户端使用
public class RestaurantDemo {
public static void main(String[] args) {
Chef chefWang = new Chef();
CookingCommand fryCmd = new StirFryCommand(chefWang);
Waiter waiter = new Waiter();
waiter.takeOrder(fryCmd);
waiter.submitOrder();
}
}
这个示例展示了命令模式的基础结构:
CookingCommand
接口定义了执行规范- 具体命令封装了厨师的炒菜操作
- 服务员作为调用者触发命令执行
- 实现了请求发起者(服务员)与执行者(厨师)的解耦
3. 支持撤销的文本编辑器示例
// 支持撤销的命令接口
interface EditorCommand {
void execute();
void undo();
}
// 文本编辑接收者
class TextEditor {
private StringBuilder content = new StringBuilder();
private int cursorPosition = 0;
public void insertText(String text) {
content.insert(cursorPosition, text);
cursorPosition += text.length();
}
public void deleteText(int length) {
int start = Math.max(cursorPosition - length, 0);
content.delete(start, cursorPosition);
cursorPosition = start;
}
public String getContent() {
return content.toString();
}
}
// 插入文字命令
class InsertCommand implements EditorCommand {
private TextEditor editor;
private String insertedText;
private int insertPosition;
public InsertCommand(TextEditor editor, String text) {
this.editor = editor;
this.insertedText = text;
}
@Override
public void execute() {
editor.insertText(insertedText);
insertPosition = editor.getContent().indexOf(insertedText);
}
@Override
public void undo() {
String current = editor.getContent();
String newContent = current.substring(0, insertPosition)
+ current.substring(insertPosition + insertedText.length());
editor.deleteText(insertedText.length());
}
}
// 命令管理器
class CommandManager {
private Stack<EditorCommand> history = new Stack<>();
private Stack<EditorCommand> redoStack = new Stack<>();
public void executeCommand(EditorCommand cmd) {
cmd.execute();
history.push(cmd);
redoStack.clear();
}
public void undo() {
if (!history.isEmpty()) {
EditorCommand cmd = history.pop();
cmd.undo();
redoStack.push(cmd);
}
}
public void redo() {
if (!redoStack.isEmpty()) {
EditorCommand cmd = redoStack.pop();
cmd.execute();
history.push(cmd);
}
}
}
这个增强版示例中:
- 引入了包含undo()方法的命令接口
- 命令对象记录执行时的必要状态
- 使用双栈结构管理操作历史
- 实现了完整的撤销/重做流程
4. 关联技术:与备忘录模式的配合
当需要支持复杂对象状态的撤销时,可以结合备忘录模式:
// 备忘录类
class EditorMemento {
private final String content;
public EditorMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
// 扩展编辑器类
class AdvancedTextEditor extends TextEditor {
public EditorMemento save() {
return new EditorMemento(getContent());
}
public void restore(EditorMemento memento) {
this.content = new StringBuilder(memento.getContent());
}
}
// 结合备忘录的命令类
class ComplexInsertCommand implements EditorCommand {
private EditorMemento snapshot;
// 执行时保存状态快照
public void execute() {
snapshot = editor.save();
editor.insertText(text);
}
// 撤销时恢复状态
public void undo() {
editor.restore(snapshot);
}
}
5. 应用场景分析
- GUI操作处理:按钮点击、菜单操作等事件处理
- 事务系统:数据库操作的原子性保证
- 工作流引擎:多步骤操作的编排与回滚
- 游戏开发:玩家操作的录制与回放
- 智能家居:设备控制指令的批处理与撤回
6. 技术优劣对比
优势:
- 降低系统耦合度
- 支持扩展新命令类型
- 方便实现复合命令(宏命令)
- 支持操作队列与日志
- 撤销/重做功能天然支持
局限性:
- 可能产生过多命令类
- 维护历史记录增加内存开销
- 复杂撤销需要额外状态管理
7. 实现注意事项
- 命令生命周期管理:及时清理已完成的命令对象
- 内存泄漏预防:使用弱引用处理大型接收者对象
- 线程安全问题:多线程环境下的命令队列同步
- 持久化支持:将命令序列化实现操作持久化
- 性能优化:对高频命令采用对象池技术
8. 总结与展望
命令模式通过将请求封装为对象,为系统带来了良好的扩展性和灵活性。在处理需要支持撤销/重做、操作队列、事务处理等场景时表现出色。在实际项目中,我们可以根据具体需求选择不同的实现策略:
- 简单场景:基础命令模式实现
- 复杂状态:结合备忘录模式
- 分布式系统:实现命令的序列化传输
- 高性能场景:采用命令对象池
未来随着响应式编程的普及,命令模式可以与事件溯源(Event Sourcing)相结合,构建更强大的状态管理系统。
评论