设计模式:命令模式

目录

[一、命令模式(Command Pattern)核心定义](#一、命令模式(Command Pattern)核心定义)

二、命令模式的核心角色

三、命令模式的典型结构与代码示例

[1. 极简代码实现(订单场景)](#1. 极简代码实现(订单场景))

[步骤 1:定义接收者(真正执行业务逻辑)](#步骤 1:定义接收者(真正执行业务逻辑))

[步骤 2:定义抽象命令接口](#步骤 2:定义抽象命令接口)

[步骤 3:实现具体命令(绑定接收者和操作)](#步骤 3:实现具体命令(绑定接收者和操作))

[步骤 4:定义调用者(触发命令执行)](#步骤 4:定义调用者(触发命令执行))

[步骤 5:客户端(组装命令、调用者、接收者)](#步骤 5:客户端(组装命令、调用者、接收者))

[2. 关键特性解析](#2. 关键特性解析)

四、命令模式的扩展特性(高级用法)

[1. 命令的撤销 / 重做(Undo/Redo)](#1. 命令的撤销 / 重做(Undo/Redo))

[2. 命令队列(批量执行)](#2. 命令队列(批量执行))

[3. 宏命令(组合命令)](#3. 宏命令(组合命令))

五、命令模式的典型应用场景

[框架中的典型案例:Spring MVC 的 Command 模式](#框架中的典型案例:Spring MVC 的 Command 模式)

[六、命令模式 vs 策略模式(易混淆对比)](#六、命令模式 vs 策略模式(易混淆对比))

七、命令模式的优缺点

优点

缺点

八、使用注意事项

总结


一、命令模式(Command Pattern)核心定义

命令模式是一种行为型设计模式 ,核心思想是:将 "请求" 封装为一个独立的对象(命令对象),这个对象包含执行请求的所有信息(如接收者、方法、参数);请求的发送者(调用者)无需知道请求的具体处理逻辑,只需调用命令对象的执行方法即可

简单来说,命令模式把 "做什么" 和 "谁去做、怎么做" 分离开:

  • 发送者(Invoker):只负责触发命令执行,不关心命令的具体实现;
  • 命令对象(Command):封装了具体的操作和接收者;
  • 接收者(Receiver):真正执行命令的对象(包含核心业务逻辑)。

核心价值:解耦请求的发起者和执行者,支持请求的参数化、队列化、撤销 / 重做等扩展。

二、命令模式的核心角色

命令模式包含 4 个核心角色(以 Java 为例),角色分工清晰且职责单一:

角色 核心职责 典型示例(订单场景)
抽象命令(Command) 定义命令的统一接口,通常包含 execute() 方法(执行命令),可选 undo()(撤销命令) OrderCommand 接口(含 execute()
具体命令(ConcreteCommand) 实现抽象命令接口,绑定 "接收者" 和 "具体操作",调用接收者的方法完成命令 CreateOrderCommand/CancelOrderCommand
调用者(Invoker) 持有命令对象,触发命令执行(无需知道命令的具体逻辑) OrderInvoker(订单处理器,调用 command.execute()
接收者(Receiver) 包含具体的业务逻辑(如创建订单、取消订单),是命令的实际执行者 OrderService(包含 createOrder()/cancelOrder()
客户端(Client) 创建命令对象,绑定接收者,将命令对象传递给调用者 业务代码中构建 CreateOrderCommand,并设置给 OrderInvoker

三、命令模式的典型结构与代码示例

1. 极简代码实现(订单场景)

以 "电商订单操作" 为例,实现命令模式的核心结构:

步骤 1:定义接收者(真正执行业务逻辑)

java

运行

复制代码
// 接收者:订单服务(包含核心业务逻辑)
public class OrderService {
    // 创建订单
    public void createOrder(String orderId, String userId) {
        System.out.println("接收者执行:创建订单,订单ID=" + orderId + ",用户ID=" + userId);
    }

    // 取消订单
    public void cancelOrder(String orderId) {
        System.out.println("接收者执行:取消订单,订单ID=" + orderId);
    }
}
步骤 2:定义抽象命令接口

java

运行

复制代码
// 抽象命令:订单操作命令
public interface OrderCommand {
    // 执行命令
    void execute();
}
步骤 3:实现具体命令(绑定接收者和操作)

java

运行

复制代码
// 具体命令1:创建订单命令
public class CreateOrderCommand implements OrderCommand {
    // 绑定接收者
    private OrderService orderService;
    // 命令所需参数
    private String orderId;
    private String userId;

    // 构造器注入接收者和参数
    public CreateOrderCommand(OrderService orderService, String orderId, String userId) {
        this.orderService = orderService;
        this.orderId = orderId;
        this.userId = userId;
    }

    @Override
    public void execute() {
        // 调用接收者的具体方法
        orderService.createOrder(orderId, userId);
    }
}

// 具体命令2:取消订单命令
public class CancelOrderCommand implements OrderCommand {
    private OrderService orderService;
    private String orderId;

    public CancelOrderCommand(OrderService orderService, String orderId) {
        this.orderService = orderService;
        this.orderId = orderId;
    }

    @Override
    public void execute() {
        orderService.cancelOrder(orderId);
    }
}
步骤 4:定义调用者(触发命令执行)

java

运行

复制代码
// 调用者:订单处理器(只负责触发命令,不关心具体逻辑)
public class OrderInvoker {
    // 持有当前要执行的命令
    private OrderCommand command;

    // 设置命令(支持动态切换命令)
    public void setCommand(OrderCommand command) {
        this.command = command;
    }

    // 执行命令
    public void executeCommand() {
        if (command != null) {
            command.execute();
        } else {
            System.out.println("未设置订单命令");
        }
    }
}
步骤 5:客户端(组装命令、调用者、接收者)

java

运行

复制代码
public class Client {
    public static void main(String[] args) {
        // 1. 创建接收者(核心业务逻辑)
        OrderService orderService = new OrderService();

        // 2. 创建具体命令(绑定接收者和参数)
        OrderCommand createCommand = new CreateOrderCommand(orderService, "ORDER001", "USER001");
        OrderCommand cancelCommand = new CancelOrderCommand(orderService, "ORDER001");

        // 3. 创建调用者
        OrderInvoker invoker = new OrderInvoker();

        // 4. 执行创建订单命令
        invoker.setCommand(createCommand);
        invoker.executeCommand(); // 输出:接收者执行:创建订单,订单ID=ORDER001,用户ID=USER001

        // 5. 执行取消订单命令(动态切换命令)
        invoker.setCommand(cancelCommand);
        invoker.executeCommand(); // 输出:接收者执行:取消订单,订单ID=ORDER001
    }
}

2. 关键特性解析

  • 解耦性 :调用者(OrderInvoker)只知道调用 execute(),无需知道是 "创建订单" 还是 "取消订单";接收者(OrderService)只负责执行业务逻辑,无需知道谁触发;
  • 命令参数化:通过构造器将参数传入命令对象,支持不同参数的命令复用;
  • 动态切换:调用者可随时切换命令(如从创建订单切换为取消订单);
  • 可扩展 :新增 "修改订单" 命令,只需新增 ModifyOrderCommand,无需修改调用者和接收者。

四、命令模式的扩展特性(高级用法)

1. 命令的撤销 / 重做(Undo/Redo)

命令模式天然支持撤销 / 重做,只需在抽象命令中新增 undo() 方法:

java

运行

复制代码
// 扩展抽象命令:支持撤销
public interface OrderCommand {
    void execute();
    void undo(); // 撤销命令
}

// 具体命令实现撤销逻辑
public class CreateOrderCommand implements OrderCommand {
    // 省略原有代码...

    @Override
    public void undo() {
        // 撤销创建订单:调用取消订单方法
        orderService.cancelOrder(orderId);
    }
}

// 调用者扩展撤销方法
public class OrderInvoker {
    // 记录已执行的命令(用于撤销)
    private List<OrderCommand> commandHistory = new ArrayList<>();

    public void executeCommand() {
        if (command != null) {
            command.execute();
            commandHistory.add(command); // 记录命令
        }
    }

    // 撤销最后一次执行的命令
    public void undoLastCommand() {
        if (!commandHistory.isEmpty()) {
            OrderCommand lastCommand = commandHistory.remove(commandHistory.size() - 1);
            lastCommand.undo();
        }
    }
}

// 客户端测试撤销
public class Client {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        OrderCommand createCommand = new CreateOrderCommand(orderService, "ORDER001", "USER001");
        OrderInvoker invoker = new OrderInvoker();

        invoker.setCommand(createCommand);
        invoker.executeCommand(); // 创建订单
        invoker.undoLastCommand(); // 撤销:取消订单
    }
}

2. 命令队列(批量执行)

调用者可维护一个命令队列,批量执行多个命令,适用于 "批量操作" 场景(如批量创建订单):

java

运行

复制代码
public class OrderInvoker {
    // 命令队列
    private Queue<OrderCommand> commandQueue = new LinkedList<>();

    // 添加命令到队列
    public void addCommand(OrderCommand command) {
        commandQueue.offer(command);
    }

    // 批量执行队列中的命令
    public void executeBatch() {
        while (!commandQueue.isEmpty()) {
            OrderCommand command = commandQueue.poll();
            command.execute();
        }
    }
}

// 客户端测试批量执行
public class Client {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        OrderInvoker invoker = new OrderInvoker();

        // 添加多个命令到队列
        invoker.addCommand(new CreateOrderCommand(orderService, "ORDER001", "USER001"));
        invoker.addCommand(new CreateOrderCommand(orderService, "ORDER002", "USER002"));
        invoker.addCommand(new CancelOrderCommand(orderService, "ORDER001"));

        // 批量执行
        invoker.executeBatch();
    }
}

3. 宏命令(组合命令)

将多个命令组合为一个 "宏命令",执行宏命令即执行所有子命令,符合 "组合模式 + 命令模式" 的结合:

java

运行

复制代码
// 宏命令:批量执行多个订单命令
public class BatchOrderCommand implements OrderCommand {
    // 子命令列表
    private List<OrderCommand> subCommands = new ArrayList<>();

    // 添加子命令
    public void addSubCommand(OrderCommand command) {
        subCommands.add(command);
    }

    @Override
    public void execute() {
        for (OrderCommand command : subCommands) {
            command.execute();
        }
    }
}

// 客户端测试宏命令
public class Client {
    public static void main(String[] args) {
        OrderService orderService = new OrderService();
        // 创建子命令
        OrderCommand cmd1 = new CreateOrderCommand(orderService, "ORDER001", "USER001");
        OrderCommand cmd2 = new CreateOrderCommand(orderService, "ORDER002", "USER002");
        // 创建宏命令
        BatchOrderCommand batchCmd = new BatchOrderCommand();
        batchCmd.addSubCommand(cmd1);
        batchCmd.addSubCommand(cmd2);

        // 执行宏命令
        batchCmd.execute();
    }
}

五、命令模式的典型应用场景

命令模式在框架和业务开发中广泛使用,核心场景是 "需要将请求封装、解耦发送者和执行者,或支持请求的队列化、撤销 / 重做":

场景 命令模式体现
Spring MVC Handler Controller 作为命令对象,封装了请求处理逻辑;DispatcherServlet 作为调用者,触发 Controller 执行(handleRequest
MyBatis Mapper Mapper 接口的方法封装为 MappedStatement(命令对象),Executor 作为调用者,触发 SQL 执行(接收者是 StatementHandler
图形界面(GUI)操作 按钮点击事件封装为命令(如 "复制""粘贴" 命令),按钮是调用者,操作系统底层是接收者,支持撤销 / 重做
任务调度(如 Quartz) 调度任务封装为 Job(命令对象),Scheduler 作为调用者,触发任务执行,支持任务队列、重试
事务操作 事务的 "提交""回滚" 封装为命令,事务管理器作为调用者,支持批量事务执行、回滚(撤销)
远程调用(RPC) 调用请求封装为命令对象(包含接口名、方法名、参数),客户端是调用者,服务端是接收者

框架中的典型案例:Spring MVC 的 Command 模式

  • 抽象命令Handler 接口(如 ControllerHttpRequestHandler);
  • 具体命令 :自定义 @Controller 类(如 UserController);
  • 调用者DispatcherServlet(调用 HandlerAdapter.handle() 执行命令);
  • 接收者Controller 内部的 Service/DAO 层(真正执行业务逻辑)。

执行流程:

plaintext

复制代码
客户端请求 → DispatcherServlet(调用者)→ 匹配 Controller(命令对象)→ Controller 调用 Service(接收者)→ 处理请求

六、命令模式 vs 策略模式(易混淆对比)

命令模式和策略模式都体现 "封装行为",但核心目标不同,需重点区分:

维度 命令模式 策略模式
核心目的 解耦 "请求发送者" 和 "执行者",支持请求的封装、队列、撤销 封装不同的算法,让算法可动态切换,聚焦 "怎么做"
角色关系 命令对象绑定接收者(执行者),调用者不依赖接收者 策略对象自身包含算法,上下文(调用者)直接依赖策略对象
行为特性 命令是 "动作"(如创建订单),可记录、撤销、批量执行 策略是 "算法"(如排序算法、支付方式),无撤销 / 队列特性
示例 按钮点击命令、订单操作命令 排序策略(冒泡 / 快排)、支付策略(微信 / 支付宝)

七、命令模式的优缺点

优点

  1. 解耦性:发送者和执行者完全解耦,新增命令无需修改调用者和接收者;
  2. 扩展性:新增命令只需实现抽象命令接口,符合 "开闭原则";
  3. 灵活性:支持命令的队列化、批量执行、撤销 / 重做、宏命令组合;
  4. 可追踪:可记录命令执行日志,便于审计和问题排查。

缺点

  1. 类膨胀:每个具体命令都需创建一个类,命令数量多会导致类数量激增;
  2. 复杂度提升:引入命令对象、调用者等角色,增加系统复杂度;
  3. 性能损耗:命令的封装和传递会带来少量性能开销(可忽略,除非高频调用)。

八、使用注意事项

  1. 避免过度设计:简单场景(如直接调用方法)无需使用命令模式,仅在需要 "解耦、撤销、队列" 时使用;
  2. 命令参数复用 :可通过命令对象的参数化,复用同一命令类处理不同参数(如 CreateOrderCommand 适配不同订单 ID);
  3. 撤销 / 重做的边界:撤销逻辑需保证幂等性(如撤销 "创建订单" 需确保订单确实存在);
  4. 结合其他模式:命令模式可与组合模式(宏命令)、备忘录模式(保存命令状态用于撤销)、责任链模式(命令传递)结合使用。

总结

命令模式的核心是 "将请求封装为对象",核心价值在于解耦请求的发起者和执行者,并支持请求的灵活扩展(队列、撤销、批量执行)。它在框架设计(Spring MVC、MyBatis)、GUI 开发、任务调度等场景中不可或缺,是解决 "请求封装与解耦" 的最优模式之一。

掌握命令模式的关键是理解 "命令对象" 的核心作用:它既是发送者和执行者之间的桥梁,也是实现 "可扩展、可追踪、可撤销" 请求处理的核心载体。

相关推荐
阿闽ooo3 小时前
抽象工厂模式实战:用C++打造家具生产系统(附UML图与完整代码)
c++·设计模式·抽象工厂模式·uml
是店小二呀3 小时前
DanceGRPO+FLUX:多模态生成强化学习模型的高效
设计模式
明洞日记3 小时前
【设计模式手册022】抽象工厂模式 - 创建产品家族
java·设计模式·抽象工厂模式
阿拉斯攀登4 小时前
设计模式:命令模式(Spring MVC中的实践)
设计模式·springmvc·命令模式
明洞日记4 小时前
【设计模式手册021】代理模式 - 如何控制对象访问
设计模式·系统安全·代理模式
山沐与山4 小时前
【设计模式】Python策略模式:从入门到实战
python·设计模式·策略模式
阿拉斯攀登4 小时前
设计模式:责任链模式(mybatis数据权限实现)
设计模式·mybatis·责任链模式
syt_10134 小时前
设计模式之-模板模式
设计模式
阿拉斯攀登4 小时前
设计模式:责任链模式(MyBatis)
设计模式·mybatis·责任链模式