命令模式 (Command Pattern) —— “万能遥控器“设计模式

🌟 核心思想

命令模式将请求封装成对象,使你可以:

  • 参数化客户端的不同请求

  • 将请求排队或记录日志

  • 支持可撤销的操作

就像现实中的遥控器,每个按钮对应一个封装好的命令,不需要知道具体实现。


🎛 现实世界类比:餐厅点餐

  • 顾客(Client):创建命令对象(点单)

  • 服务员(Invoker):接收并传递命令(订单)

  • 厨师(Receiver):知道如何执行请求(做菜)

  • 订单(Command):封装了具体的请求


🏗 四大核心角色

角色作用餐厅示例
Command(命令)声明执行操作的接口订单接口
ConcreteCommand(具体命令)绑定Receiver和动作具体菜品订单
Receiver(接收者)知道如何执行操作厨师
Invoker(调用者)触发命令执行服务员
Client(客户端)创建具体命令顾客

📜 代码示例(Java实现)

1. 定义命令接口和具体命令

java

复制

// 命令接口
interface Command {
    void execute();
    void undo(); // 支持撤销
}

// 具体命令:做披萨
class PizzaOrder implements Command {
    private Chef receiver;
    private String pizzaType;
    
    public PizzaOrder(Chef receiver, String pizzaType) {
        this.receiver = receiver;
        this.pizzaType = pizzaType;
    }
    
    public void execute() {
        receiver.makePizza(pizzaType);
    }
    
    public void undo() {
        receiver.cancelPizza(pizzaType);
    }
}

// 具体命令:做意面
class PastaOrder implements Command {
    private Chef receiver;
    private String pastaType;
    
    public PastaOrder(Chef receiver, String pastaType) {
        this.receiver = receiver;
        this.pastaType = pastaType;
    }
    
    public void execute() {
        receiver.makePasta(pastaType);
    }
    
    public void undo() {
        receiver.cancelPasta(pastaType);
    }
}

2. 定义接收者(厨师)

java

复制

// 接收者:知道如何执行操作
class Chef {
    public void makePizza(String type) {
        System.out.println("制作" + type + "披萨");
    }
    
    public void cancelPizza(String type) {
        System.out.println("取消" + type + "披萨订单");
    }
    
    public void makePasta(String type) {
        System.out.println("制作" + type + "意面");
    }
    
    public void cancelPasta(String type) {
        System.out.println("取消" + type + "意面订单");
    }
}

3. 定义调用者(服务员)

java

复制

import java.util.Stack;

// 调用者:触发命令执行
class Waiter {
    private Stack<Command> history = new Stack<>();
    
    public void takeOrder(Command command) {
        command.execute();
        history.push(command); // 记录历史用于撤销
    }
    
    public void cancelLastOrder() {
        if (!history.isEmpty()) {
            Command last = history.pop();
            last.undo();
        }
    }
}

4. 客户端使用

java

复制

public class Restaurant {
    public static void main(String[] args) {
        Chef chef = new Chef(); // 接收者
        
        // 创建具体命令
        Command pizza = new PizzaOrder(chef, "海鲜");
        Command pasta = new PastaOrder(chef, "番茄肉酱");
        
        Waiter waiter = new Waiter(); // 调用者
        
        // 点餐执行命令
        waiter.takeOrder(pizza);
        waiter.takeOrder(pasta);
        
        System.out.println("\n--- 取消最后一个订单 ---");
        waiter.cancelLastOrder();
    }
}

输出结果:

复制

制作海鲜披萨
制作番茄肉酱意面

--- 取消最后一个订单 ---
取消番茄肉酱意面订单

✅ 优点

✔ 解耦调用者和接收者:调用者无需知道具体实现
✔ 可扩展性强:新增命令不影响现有代码
✔ 支持撤销/重做:维护命令历史记录
✔ 支持宏命令:组合多个命令为一个


❌ 缺点

✖ 可能产生大量命令类:每个操作都需要一个类
✖ 增加系统复杂度:多层间接调用


🎯 适用场景

  • 需要回调机制的系统

  • 需要实现撤销/重做功能

  • 需要将操作排队、记录或远程执行

  • 需要支持事务的系统

典型应用:

  • 图形界面菜单/按钮

  • 游戏控制指令

  • 数据库事务

  • 智能家居遥控系统


🔄 对比其他模式

模式目的关键区别
命令模式封装请求为对象强调请求的封装与执行分离
策略模式封装算法关注算法替换,通常无接收者
职责链模式处理请求链多个对象尝试处理同一请求

模式进阶技巧

  1. 组合命令:创建宏命令(一次执行多个命令)

  2. 命令队列:实现异步命令执行

  3. 日志记录:持久化命令序列用于恢复

  4. 事务处理:实现原子操作(全部成功或全部回滚)


总结

命令模式就像万能遥控器,将各种操作封装成统一接口的命令对象,带来三大超能力:

  1. 一键操作:隐藏复杂实现细节

  2. 时光倒流:轻松实现撤销功能

  3. 批处理:支持命令组合和队列

💡 设计箴言:"把请求变成对象,让控制更加灵活" —— 命令模式精髓

Invoker(调用者)与 Client(客户端)的关系解析

在命令模式中,Invoker 和 Client 是两个关键但角色不同的参与者,它们的关系可以用"餐厅后厨系统"来形象理解:

🍽️ 形象比喻

  • Client(顾客):点餐的人(知道想吃什么,但不知道怎么做)

  • Invoker(服务员):传菜员(不关心菜怎么做,只负责触发命令传递)

  • Command(订单):记录客户需求的单据

  • Receiver(厨师):实际执行操作的人

🔍 核心关系特点

维度InvokerClient二者关系
职责触发命令执行创建并配置命令对象Client组装命令,Invoker执行命令
知情权不知道命令具体实现知道Receiver和具体命令Client是"知情者",Invoker是"盲执行者"
交互方式持有命令接口引用创建具体命令实例Client将具体命令注入Invoker
类比遥控器按钮编程遥控器的人就像给遥控器编程的人(Client)和实际按按钮的人(Invoker)的关系

💻 代码层面关系

java

复制

// Client的典型操作:
Receiver chef = new Chef(); // 1. 创建接收者
Command cmd = new PizzaOrder(chef, "海鲜"); // 2. 创建具体命令
Invoker waiter = new Waiter(); // 3. 创建调用者

waiter.setCommand(cmd); // 4. Client将命令装配给Invoker
waiter.executeCommand(); // 5. Invoker执行命令(不知道具体是什么命令)

⚡ 交互流程图

复制

Client → 创建Receiver → 创建ConcreteCommand → 绑定到Invoker → Invoker.execute()
      (知情者)          (组装命令)             (注入依赖)       (盲执行)

🌟 关键设计原则体现

  1. 单一职责原则

    • Client专注命令装配

    • Invoker专注命令触发

  2. 松耦合

    • Invoker只依赖Command抽象接口

    • 二者通过命令对象间接通信

  3. 控制反转

    • Client控制流程(什么时候创建/绑定命令)

    • Invoker被注入命令后执行

🛠️ 实际应用场景

  1. GUI按钮系统

    • Client:界面构建代码

    • Invoker:按钮组件

    • Command:点击操作

  2. 游戏控制

    • Client:游戏初始化代码

    • Invoker:输入处理器

    • Command:游戏指令

  3. 事务系统

    • Client:事务组装代码

    • Invoker:事务执行引擎

    • Command:子操作命令

⚠️ 常见误区

  1. 错误认知:认为Invoker需要知道具体命令类型

    • 实际上Invoker应该只依赖Command接口

  2. 过度设计:简单场景可能不需要严格区分二者

    • 当Client本身就能直接调用Receiver时,可能不需要完整命令模式

  3. 生命周期混淆

    • Client通常只在初始化阶段活跃

    • Invoker在运行时持续接收触发

这种精妙的职责分离正是命令模式强大灵活性的核心所在!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值