【从零入门23种设计模式14】行为型之命令模式

📅 发布时间:2026/7/9 4:32:11 👁️ 浏览次数:
【从零入门23种设计模式14】行为型之命令模式
一、命令模式的核心本质命令模式是行为型设计模式的核心核心定义将请求操作 / 行为封装为独立对象使请求的发起者调用者与请求的执行者接收者完全解耦同时支持请求的参数化、队列化、可撤销、可记录、可异步执行。核心解决的问题解耦调用者如 Controller无需知道接收者如 OrderService的实现细节只需调用命令的execute()方法扩展新增操作时只需新增命令类无需修改原有代码开闭原则管控可对请求做统一管控队列、延迟、撤销、日志、事务。标准角色角色职责类比餐厅场景代码定位抽象命令Command定义命令的统一接口核心execute()可选undo()/redo()菜单模板规定点菜格式接口 / 抽象类具体命令ConcreteCommand实现抽象命令持有接收者引用调用接收者的具体方法具体菜品鱼香肉丝实现类如CreateOrderCommand接收者Receiver真正执行业务逻辑的对象核心业务代码厨师真正做菜的人业务 Service如OrderService调用者Invoker管理 / 触发命令执行可批量、队列、延迟执行命令服务员传递菜单给厨师Controller / 任务调度器 / 队列客户端Client创建命令对象绑定接收者交给调用者执行顾客点菜并交给服务员业务入口如接口调用核心 UML 类图二、基础版完整实现吃透核心结构以 “智能家居遥控” 为例实现灯、空调的开关命令支持执行 / 撤销覆盖命令模式所有核心角色。1. 步骤 1定义接收者真正干活的对象/** * 接收者1灯真正执行开关逻辑 */ public class Light { private String name; // 灯的名称参数化 public Light(String name) { this.name name; } // 核心业务逻辑开灯 public void on() { System.out.println(【接收者】 name 灯 → 打开); } // 核心业务逻辑关灯 public void off() { System.out.println(【接收者】 name 灯 → 关闭); } } /** * 接收者2空调真正执行开关逻辑 */ public class AirConditioner { private String location; // 空调位置参数化 public AirConditioner(String location) { this.location location; } // 核心业务逻辑开空调 public void on() { System.out.println(【接收者】 location 空调 → 打开); } // 核心业务逻辑关空调 public void off() { System.out.println(【接收者】 location 空调 → 关闭); } }2. 步骤 2定义抽象命令统一接口/** * 抽象命令定义执行/撤销接口命令模式核心 */ public interface SmartHomeCommand { /** * 执行命令 */ void execute(); /** * 撤销命令 */ void undo(); }3. 步骤 3实现具体命令绑定接收者/** * 具体命令1开灯命令 */ public class LightOnCommand implements SmartHomeCommand { // 持有接收者引用核心命令封装接收者和具体动作 private Light light; // 构造函数绑定接收者参数化命令 public LightOnCommand(Light light) { this.light light; } Override public void execute() { light.on(); // 调用接收者的具体方法 } Override public void undo() { light.off(); // 撤销执行反向操作 } } /** * 具体命令2关灯命令 */ public class LightOffCommand implements SmartHomeCommand { private Light light; public LightOffCommand(Light light) { this.light light; } Override public void execute() { light.off(); } Override public void undo() { light.on(); } } /** * 具体命令3开空调命令 */ public class AirConditionerOnCommand implements SmartHomeCommand { private AirConditioner ac; public AirConditionerOnCommand(AirConditioner ac) { this.ac ac; } Override public void execute() { ac.on(); } Override public void undo() { ac.off(); } }4. 步骤 4实现调用者遥控器管理命令import java.util.Stack; /** * 调用者智能家居遥控器管理命令的执行/撤销 */ public class SmartHomeRemote { // 当前执行的命令支持单命令 private SmartHomeCommand currentCommand; // 命令历史栈支持撤销/重做 private StackSmartHomeCommand commandHistory new Stack(); /** * 设置并执行命令核心调用者无需知道命令细节 */ public void setAndExecuteCommand(SmartHomeCommand command) { this.currentCommand command; command.execute(); // 执行命令 commandHistory.push(command); // 记录历史用于撤销 } /** * 撤销上一个命令 */ public void undoLastCommand() { if (!commandHistory.isEmpty()) { SmartHomeCommand lastCommand commandHistory.pop(); lastCommand.undo(); // 执行撤销 System.out.println(【调用者】撤销上一个命令); } else { System.out.println(【调用者】无命令可撤销); } } }5. 步骤 5客户端调用完整流程/** * 客户端用户操作遥控器命令模式入口 */ public class CommandClient { public static void main(String[] args) { // 1. 创建接收者具体设备 Light livingRoomLight new Light(客厅); AirConditioner bedroomAc new AirConditioner(卧室); // 2. 创建具体命令绑定接收者 SmartHomeCommand lightOn new LightOnCommand(livingRoomLight); SmartHomeCommand acOn new AirConditionerOnCommand(bedroomAc); SmartHomeCommand lightOff new LightOffCommand(livingRoomLight); // 3. 创建调用者遥控器 SmartHomeRemote remote new SmartHomeRemote(); // 4. 执行命令调用者只需调用命令无需知道底层逻辑 System.out.println( 执行开灯命令 ); remote.setAndExecuteCommand(lightOn); System.out.println(\n 执行开空调命令 ); remote.setAndExecuteCommand(acOn); System.out.println(\n 执行关灯命令 ); remote.setAndExecuteCommand(lightOff); System.out.println(\n 撤销最后一个命令关灯→开灯 ); remote.undoLastCommand(); System.out.println(\n 再次撤销开空调→关空调 ); remote.undoLastCommand(); } }输出结果完整流程 执行开灯命令 【接收者】客厅灯 → 打开 执行开空调命令 【接收者】卧室空调 → 打开 执行关灯命令 【接收者】客厅灯 → 关闭 撤销最后一个命令关灯→开灯 【接收者】客厅灯 → 打开 【调用者】撤销上一个命令 再次撤销开空调→关空调 【接收者】卧室空调 → 关闭 【调用者】撤销上一个命令三、Spring 企业级实战版落地生产环境命令模式在 Spring 项目中最核心的场景是业务操作的标准化、可管控、可扩展如订单操作、支付流程、审批流程。以下实现 “订单生命周期命令链”支持批量执行、事务控制、日志记录。1. 依赖准备Spring Bootdependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId version3.2.3/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies2. 核心模型定义import lombok.Data; /** * 命令上下文传递命令执行所需参数参数化命令 */ Data public class OrderCommandContext { private String orderId; // 订单ID private String userId; // 用户ID private double amount; // 订单金额 private String remark; // 备注 // 可扩展添加更多参数如支付方式、物流信息 } /** * 命令执行结果 */ Data public class CommandResult { private boolean success; // 是否成功 private String message; // 结果信息 private String orderId; // 关联订单ID // 快捷创建方法 public static CommandResult success(String orderId, String message) { CommandResult result new CommandResult(); result.setSuccess(true); result.setOrderId(orderId); result.setMessage(message); return result; } public static CommandResult fail(String orderId, String message) { CommandResult result new CommandResult(); result.setSuccess(false); result.setOrderId(orderId); result.setMessage(message); return result; } }3. 抽象命令Spring 管理/** * 订单抽象命令Spring Bean */ public interface OrderCommand { /** * 执行订单命令 * param context 命令上下文 * return 执行结果 */ CommandResult execute(OrderCommandContext context); /** * 获取命令类型用于日志/管控 */ String getCommandType(); }4. 接收者核心业务 Serviceimport lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * 接收者订单核心业务Service真正执行业务逻辑 */ Slf4j Service public class OrderBizService { /** * 创建订单核心业务 */ public CommandResult createOrder(OrderCommandContext context) { log.info(【接收者-业务逻辑】创建订单 | 订单ID{} | 用户ID{} | 金额{}, context.getOrderId(), context.getUserId(), context.getAmount()); // 模拟业务逻辑如插入数据库、扣减库存 return CommandResult.success(context.getOrderId(), 订单创建成功); } /** * 支付订单核心业务 */ public CommandResult payOrder(OrderCommandContext context) { log.info(【接收者-业务逻辑】支付订单 | 订单ID{} | 金额{}, context.getOrderId(), context.getAmount()); // 模拟业务逻辑如调用支付接口、更新订单状态 if (context.getAmount() 0) { return CommandResult.fail(context.getOrderId(), 支付金额无效); } return CommandResult.success(context.getOrderId(), 订单支付成功); } /** * 取消订单核心业务 */ public CommandResult cancelOrder(OrderCommandContext context) { log.info(【接收者-业务逻辑】取消订单 | 订单ID{} | 备注{}, context.getOrderId(), context.getRemark()); // 模拟业务逻辑如恢复库存、退款 return CommandResult.success(context.getOrderId(), 订单取消成功); } }5. 具体命令实现Spring Beanimport lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 具体命令1创建订单命令 */ Slf4j Component RequiredArgsConstructor // 构造函数注入Spring推荐 public class CreateOrderCommand implements OrderCommand { // 注入接收者核心命令绑定接收者 private final OrderBizService orderBizService; Override public CommandResult execute(OrderCommandContext context) { log.info(【具体命令】开始执行创建订单命令 | 订单ID{}, context.getOrderId()); return orderBizService.createOrder(context); } Override public String getCommandType() { return CREATE_ORDER; } } /** * 具体命令2支付订单命令 */ Slf4j Component RequiredArgsConstructor public class PayOrderCommand implements OrderCommand { private final OrderBizService orderBizService; Override public CommandResult execute(OrderCommandContext context) { log.info(【具体命令】开始执行支付订单命令 | 订单ID{}, context.getOrderId()); return orderBizService.payOrder(context); } Override public String getCommandType() { return PAY_ORDER; } } /** * 具体命令3取消订单命令 */ Slf4j Component RequiredArgsConstructor public class CancelOrderCommand implements OrderCommand { private final OrderBizService orderBizService; Override public CommandResult execute(OrderCommandContext context) { log.info(【具体命令】开始执行取消订单命令 | 订单ID{}, context.getOrderId()); return orderBizService.cancelOrder(context); } Override public String getCommandType() { return CANCEL_ORDER; } }6. 调用者命令管理器支持批量 / 事务import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * 调用者订单命令管理器统一管控命令执行 */ Slf4j Component RequiredArgsConstructor public class OrderCommandManager { // 注入所有订单命令Spring自动扫描 private final ListOrderCommand orderCommands; /** * 执行单个命令基础 */ public CommandResult executeCommand(OrderCommand command, OrderCommandContext context) { log.info(【调用者】开始执行命令 | 命令类型{} | 订单ID{}, command.getCommandType(), context.getOrderId()); long startTime System.currentTimeMillis(); try { CommandResult result command.execute(context); log.info(【调用者】命令执行完成 | 耗时{}ms | 结果{}, System.currentTimeMillis() - startTime, result.isSuccess()); return result; } catch (Exception e) { log.error(【调用者】命令执行异常 | 命令类型{} | 订单ID{}, command.getCommandType(), context.getOrderId(), e); return CommandResult.fail(context.getOrderId(), 命令执行异常 e.getMessage()); } } /** * 批量执行命令事务控制 * 核心要么全部成功要么全部回滚 */ Transactional(rollbackFor Exception.class) public void executeBatchCommand(ListOrderCommand commands, OrderCommandContext context) { log.info(【调用者】开始批量执行命令 | 订单ID{} | 命令数量{}, context.getOrderId(), commands.size()); for (OrderCommand command : commands) { CommandResult result executeCommand(command, context); if (!result.isSuccess()) { // 抛出异常触发事务回滚 throw new RuntimeException(批量命令执行失败 | 命令类型 command.getCommandType() | 原因 result.getMessage()); } } log.info(【调用者】批量命令执行成功 | 订单ID{}, context.getOrderId()); } }7. 客户端Spring Boot 测试import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import java.util.Arrays; import java.util.List; SpringBootApplication public class SpringCommandDemoApplication { public static void main(String[] args) { // 1. 启动Spring容器 ConfigurableApplicationContext context SpringApplication.run(SpringCommandDemoApplication.class, args); // 2. 获取调用者和具体命令 OrderCommandManager commandManager context.getBean(OrderCommandManager.class); CreateOrderCommand createCommand context.getBean(CreateOrderCommand.class); PayOrderCommand payCommand context.getBean(PayOrderCommand.class); CancelOrderCommand cancelCommand context.getBean(CancelOrderCommand.class); // 3. 测试1执行单个创建订单命令 System.out.println( 测试1执行单个创建订单命令 ); OrderCommandContext context1 new OrderCommandContext(); context1.setOrderId(O20240308001); context1.setUserId(U10086); context1.setAmount(999.9); CommandResult result1 commandManager.executeCommand(createCommand, context1); System.out.println(执行结果 result1); // 4. 测试2执行单个支付订单命令 System.out.println(\n 测试2执行单个支付订单命令 ); OrderCommandContext context2 new OrderCommandContext(); context2.setOrderId(O20240308001); context2.setAmount(999.9); CommandResult result2 commandManager.executeCommand(payCommand, context2); System.out.println(执行结果 result2); // 5. 测试3批量执行命令创建支付 System.out.println(\n 测试3批量执行命令 ); OrderCommandContext context3 new OrderCommandContext(); context3.setOrderId(O20240308002); context3.setUserId(U10010); context3.setAmount(1999.9); ListOrderCommand batchCommands Arrays.asList(createCommand, payCommand); try { commandManager.executeBatchCommand(batchCommands, context3); System.out.println(批量命令执行成功); } catch (Exception e) { System.out.println(批量命令执行失败 e.getMessage()); } // 6. 测试4执行取消订单命令 System.out.println(\n 测试4执行取消订单命令 ); OrderCommandContext context4 new OrderCommandContext(); context4.setOrderId(O20240308002); context4.setRemark(用户主动取消); CommandResult result4 commandManager.executeCommand(cancelCommand, context4); System.out.println(执行结果 result4); context.close(); } }输出结果生产级流程 测试1执行单个创建订单命令 【调用者】开始执行命令 | 命令类型CREATE_ORDER | 订单IDO20240308001 【具体命令】开始执行创建订单命令 | 订单IDO20240308001 【接收者-业务逻辑】创建订单 | 订单IDO20240308001 | 用户IDU10086 | 金额999.9 【调用者】命令执行完成 | 耗时10ms | 结果true 执行结果CommandResult(successtrue, message订单创建成功, orderIdO20240308001) 测试2执行单个支付订单命令 【调用者】开始执行命令 | 命令类型PAY_ORDER | 订单IDO20240308001 【具体命令】开始执行支付订单命令 | 订单IDO20240308001 【接收者-业务逻辑】支付订单 | 订单IDO20240308001 | 金额999.9 【调用者】命令执行完成 | 耗时5ms | 结果true 执行结果CommandResult(successtrue, message订单支付成功, orderIdO20240308001) 测试3批量执行命令 【调用者】开始批量执行命令 | 订单IDO20240308002 | 命令数量2 【调用者】开始执行命令 | 命令类型CREATE_ORDER | 订单IDO20240308002 【具体命令】开始执行创建订单命令 | 订单IDO20240308002 【接收者-业务逻辑】创建订单 | 订单IDO20240308002 | 用户IDU10010 | 金额1999.9 【调用者】命令执行完成 | 耗时8ms | 结果true 【调用者】开始执行命令 | 命令类型PAY_ORDER | 订单IDO20240308002 【具体命令】开始执行支付订单命令 | 订单IDO20240308002 【接收者-业务逻辑】支付订单 | 订单IDO20240308002 | 金额1999.9 【调用者】命令执行完成 | 耗时3ms | 结果true 【调用者】批量命令执行成功 | 订单IDO20240308002 批量命令执行成功 测试4执行取消订单命令 【调用者】开始执行命令 | 命令类型CANCEL_ORDER | 订单IDO20240308002 【具体命令】开始执行取消订单命令 | 订单IDO20240308002 【接收者-业务逻辑】取消订单 | 订单IDO20240308002 | 备注用户主动取消 【调用者】命令执行完成 | 耗时4ms | 结果true 执行结果CommandResult(successtrue, message订单取消成功, orderIdO20240308002)四、命令模式的核心扩展能力实战必用1. 撤销 / 重做Undo/Redo原理通过栈记录命令历史撤销时执行undo()重做时重新执行execute()场景编辑类系统如文档编辑、表单操作、交易回滚注意需保证undo()是execute()的反向操作如创建→删除、支付→退款。2. 命令队列Command Queue原理将命令放入队列异步 / 有序执行场景高并发下单、消息队列消费、定时任务Spring 实现结合ThreadPoolTaskExecutor异步执行命令。3. 日志 / 快照Log/Snapshot原理记录所有命令的执行日志系统崩溃时重新执行命令恢复状态场景金融交易、电商订单、游戏存档Spring 实现通过 AOP 拦截命令执行自动记录日志。4. 事务控制Transaction原理批量命令执行时通过Transactional保证原子性场景订单创建 库存扣减 支付的组合操作注意命令执行异常时需抛出异常触发事务回滚。五、Spring 框架中的原生应用必须知道Spring 框架内部大量使用命令模式以下是核心场景1. Spring Task定时任务抽象命令Runnable/Callable具体命令自定义定时任务调用者ThreadPoolTaskScheduler接收者任务执行的业务逻辑。2. Spring JdbcTemplate抽象命令PreparedStatementCreator具体命令自定义 SQL 执行逻辑调用者JdbcTemplate接收者数据库驱动真正执行 SQL。3. Spring Batch批处理抽象命令Step/Tasklet具体命令批处理任务调用者JobLauncher接收者批处理业务逻辑。4. Spring 事件驱动ApplicationEvent抽象命令ApplicationEvent具体命令自定义事件调用者ApplicationEventPublisher接收者ApplicationListener事件监听者。六、命令模式的优缺点与适用场景优点完全解耦调用者与接收者无直接依赖可独立扩展高度灵活支持撤销、队列、异步、事务、日志等扩展能力符合开闭原则新增命令只需新增类无需修改原有代码参数化请求可通过上下文传递不同参数实现命令复用。缺点类膨胀每个操作都需创建独立命令类简单场景会增加代码量调试复杂命令链执行时需跟踪多个类的调用流程性能损耗多层封装会带来轻微的性能开销可忽略除非超高频场景。适用场景需要撤销 / 重做文档编辑、表单操作、交易回滚需要队列 / 异步执行高并发下单、消息队列、定时任务需要事务控制组合业务操作创建订单 支付 扣库存需要解耦调用者和执行者GUI 按钮操作、远程命令执行、框架扩展需要记录操作日志金融交易、审计日志、系统恢复。七、命令模式 vs 策略模式易混淆点维度命令模式策略模式核心目的封装操作 / 请求做什么封装算法 / 方案怎么做核心能力执行、撤销、队列、日志、事务算法切换、最优方案选择角色关系命令绑定接收者执行者策略无绑定直接实现算法扩展方向新增操作如新增 “退款订单” 命令新增算法如新增 “微信支付” 策略典型场景订单操作、定时任务、批处理支付方式选择、排序算法、缓存策略总结命令模式的核心是将操作封装为独立对象实现调用者与接收者的完全解耦核心角色包括抽象命令、具体命令、接收者、调用者缺一不可企业级实战中命令模式常用于订单操作、支付流程、定时任务等场景结合 Spring 可实现事务、异步、日志等管控能力命令模式的核心价值是灵活扩展撤销、队列、事务而非简单的代码解耦。