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);
        }
    }
}

这个增强版示例中:

  1. 引入了包含undo()方法的命令接口
  2. 命令对象记录执行时的必要状态
  3. 使用双栈结构管理操作历史
  4. 实现了完整的撤销/重做流程

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. 应用场景分析

  1. GUI操作处理:按钮点击、菜单操作等事件处理
  2. 事务系统:数据库操作的原子性保证
  3. 工作流引擎:多步骤操作的编排与回滚
  4. 游戏开发:玩家操作的录制与回放
  5. 智能家居:设备控制指令的批处理与撤回

6. 技术优劣对比

优势:

  • 降低系统耦合度
  • 支持扩展新命令类型
  • 方便实现复合命令(宏命令)
  • 支持操作队列与日志
  • 撤销/重做功能天然支持

局限性:

  • 可能产生过多命令类
  • 维护历史记录增加内存开销
  • 复杂撤销需要额外状态管理

7. 实现注意事项

  1. 命令生命周期管理:及时清理已完成的命令对象
  2. 内存泄漏预防:使用弱引用处理大型接收者对象
  3. 线程安全问题:多线程环境下的命令队列同步
  4. 持久化支持:将命令序列化实现操作持久化
  5. 性能优化:对高频命令采用对象池技术

8. 总结与展望

命令模式通过将请求封装为对象,为系统带来了良好的扩展性和灵活性。在处理需要支持撤销/重做、操作队列、事务处理等场景时表现出色。在实际项目中,我们可以根据具体需求选择不同的实现策略:

  • 简单场景:基础命令模式实现
  • 复杂状态:结合备忘录模式
  • 分布式系统:实现命令的序列化传输
  • 高性能场景:采用命令对象池

未来随着响应式编程的普及,命令模式可以与事件溯源(Event Sourcing)相结合,构建更强大的状态管理系统。