代码界的「遥控器大师」:命令模式的魔法卷轴

78 阅读4分钟

代码界的「遥控器大师」:命令模式的魔法卷轴


一、当操作开始「排队领号」

你是否经历过这样的混乱场面?
客厅遥控器有20个按钮,每个都直连不同家电,按错一个全家电器集体蹦迪;
游戏角色同时执行「跳跃+射击+换弹」,结果卡成PPT;
想给老板演示功能,手忙脚乱点错十个步骤...

命令模式就像代码界的万能遥控器——「别直接动手!把操作写进任务卡,让按钮成为魔法卷轴!」 通过将请求封装成对象,让每个操作都能排队、撤销,甚至倒放!


二、遥控器的魔法图纸(UML图)

          ┌─────────────┐          ┌─────────────┐  
          │   Client    │          │  Invoker    │  
          ├─────────────┤          ├─────────────┤  
          │             │─────────>│ +execute()  │  
          └──────△──────┘         └──────┬──────┘  
                 │                        │  
          ┌──────┴──────┐          ┌──────▼──────┐  
          │  Command    │<─────────┤  Receiver   │  
          ├─────────────┤          ├─────────────┤  
          │ +execute()  │          │ +action()   │  
          └─────────────┘          └─────────────┘  
  • 甲方爸爸(Client):创建具体命令
  • 秘书(Invoker):触发命令执行(如遥控器按钮)
  • 任务卡(Command):封装操作的魔法卷轴
  • 打工人(Receiver):真正干活的执行者

三、智能家居的魔法遥控(代码实战)

1. 定义打工人接口(家电基类)
// 家电打工人:所有设备必须会干活  
interface HomeDevice {  
    void on();  
    void off();  
}  

// 具体打工人:智能灯  
class SmartLight implements HomeDevice {  
    public void on() { System.out.println("💡 灯光亮起,氛围值+50"); }  
    public void off() { System.out.println("🌑 灯光熄灭,进入省电模式"); }  
}  

// 具体打工人:空调  
class AirConditioner implements HomeDevice {  
    public void on() { System.out.println("❄️ 空调启动,26℃清凉世界"); }  
    public void off() { System.out.println("🔥 空调关闭,准备出汗吧"); }  
}  
2. 编写魔法卷轴(命令对象)
// 魔法卷轴基类(所有命令的模板)  
abstract class DeviceCommand {  
    protected HomeDevice device;  
    public DeviceCommand(HomeDevice device) { this.device = device; }  
    public abstract void execute();  
    public abstract void undo(); // 撤销功能  
}  

// 开机咒语  
class TurnOnCommand extends DeviceCommand {  
    public TurnOnCommand(HomeDevice device) { super(device); }  
    public void execute() { device.on(); }  
    public void undo() { device.off(); }  
}  

// 关机咒语  
class TurnOffCommand extends DeviceCommand {  
    public TurnOffCommand(HomeDevice device) { super(device); }  
    public void execute() { device.off(); }  
    public void undo() { device.on(); }  
}  
3. 制作万能遥控器(调用者)
class MagicRemote {  
    private Map<String, DeviceCommand> buttons = new HashMap<>();  
    private Stack<DeviceCommand> history = new Stack<>(); // 操作历史  

    // 绑定按钮  
    public void setCommand(String button, DeviceCommand cmd) {  
        buttons.put(button, cmd);  
    }  

    // 按下按钮  
    public void pressButton(String button) {  
        DeviceCommand cmd = buttons.get(button);  
        if (cmd != null) {  
            cmd.execute();  
            history.push(cmd); // 记录操作  
        }  
    }  

    // 撤销按钮  
    public void pressUndo() {  
        if (!history.isEmpty()) {  
            DeviceCommand cmd = history.pop();  
            cmd.undo();  
        }  
    }  
}  
4. 来场智能家居秀
public class SmartHome {  
    public static void main(String[] args) {  
        // 初始化设备  
        HomeDevice light = new SmartLight();  
        HomeDevice ac = new AirConditioner();  

        // 配置遥控器  
        MagicRemote remote = new MagicRemote();  
        remote.setCommand("A", new TurnOnCommand(light));  
        remote.setCommand("B", new TurnOffCommand(light));  
        remote.setCommand("C", new TurnOnCommand(ac));  

        // 秀操作!  
        remote.pressButton("A"); // 开灯  
        remote.pressButton("C"); // 开空调  
        remote.pressUndo();       // 撤销开空调 → 关空调  
        remote.pressUndo();       // 撤销开灯 → 关灯  
    }  
}  

四、遥控器 vs 开关直连:魔法与物理的区别

维度命令模式直接调用
解耦程度调用者与执行者完全解耦紧密耦合
扩展性轻松支持撤销/重做需额外实现历史记录
灵活性命令可组合成宏命令难以批量操作
复杂度需要创建命令类简单直接
现实类比外卖订单系统顾客直接进厨房做饭

五、代码遥控器的真实战场

  1. GUI菜单项:每个按钮点击对应一个命令对象
  2. 游戏控制:WASD按键映射到角色移动命令
  3. 事务系统:数据库操作支持回滚
  4. 任务队列:异步处理网络请求队列
  5. 宏命令:一键执行「保存+格式化+提交代码」

冷知识
Photoshop的历史记录面板就是命令模式的经典应用,每个操作都是一个可撤销的命令对象!


六、防魔法反噬指南

  1. 避免命令爆炸
当命令超过50个时,考虑用「参数化命令」  
例:CreateFileCommand、DeleteFileCommand → FileOperationCommand(type)  
  1. 支持复合命令
class MacroCommand implements DeviceCommand {  
    List<DeviceCommand> commands = new ArrayList<>();  

    void addCommand(DeviceCommand cmd) { commands.add(cmd); }  

    public void execute() {  
        commands.forEach(DeviceCommand::execute);  
    }  

    public void undo() {  
        // 反向执行撤销  
        Collections.reverse(commands);  
        commands.forEach(DeviceCommand::undo);  
    }  
}  
  1. 线程安全处理
// 多线程环境加锁  
public synchronized void pressButton(String button) {  
    // ...  
}  
  1. 命令持久化
// 将命令序列化为JSON,支持断点续传  
String saveCommands() {  
    return new Gson().toJson(history);  
}  
  1. 与责任链模式联用
// 命令执行前经过权限校验链  
command = new AuthFilterCommand(command);  
command = new LogCommand(command);  
command.execute();  

七、魔法学院毕业总结

命令模式让代码成为时间管理大师:

  • :用于需要解耦调用与执行的场景
  • :支持撤销、队列、日志等扩展
  • 不要:为每个简单操作创建命令类
  • 不要:忽视命令对象的生命周期管理

当你在IDE中按下Ctrl+Z时,请想起命令模式——那个默默记录你每个操作的数字时光机!