📌 PDF:AI人工智能 — 大模型微调与部署实战项目
大白话说Java设计模式-34-命令模式(业务实战篇):大白商城订单撤销/重做的"操作封装"
📌 一句话本质:命令模式就是"把请求封装为对象",让请求可以排队、撤销、重做、记录日志。
🏷️ 标签:命令模式 / Java 设计模式 / 撤销重做 / 事务封装 / 大白商城 🎯 适合:初中级后端 / 经常被"操作封装"困扰的工程师
目录
- 一、业务场景引入:为什么需要"操作封装"?
- 二、反面教材:硬编码调用的"灾难现场"
- 三、模式原理:命令的"命令对象 + 调用者 + 接收者"
- 四、实战代码:大白商城订单命令完整实现
- 五、命令 vs 策略 / 责任链
- 六、JDK 的 Runnable 怎么用命令?
- 七、Spring @Transactional 怎么用命令?
- 八、工程决策 Checklist
- 九、与其他模式协作
- 十、本篇小结 + 下篇预告
一、业务场景引入:为什么需要"操作封装"?
大白商城 2024 年运营要求一个新功能:
“用户下单后 30 分钟内可撤销,撤销后 1 小时内可重做,要支持撤销/重做栈!”
我打开代码一看:
/**
* ❌ 大白商城老的下单代码
*/
public class OrderServiceV0 {
public void pay(String orderId) {
// ❌ 业务逻辑直接调,没法撤销
orderDao.updateStatus(orderId, "PAID");
stockService.decrease(...);
paymentService.pay(...);
}
public void cancel(String orderId) {
// ❌ 撤销时无法回滚
orderDao.updateStatus(orderId, "CANCELLED");
stockService.increase(...);
paymentService.refund(...);
}
}
这种代码的痛点:
| ① | 无法撤销 | 操作不可回退 |
| ② | 无法重做 | 操作不可重放 |
| ③ | 无法记录日志 | 操作不可审计 |
| ④ | 无法队列化 | 操作不可异步 |
老板要的"操作可撤销/可重做/可排队",怎么搞?
答案就是——命令模式。
1.1 大白话讲透命令
继续打比方:
场景:用户下单后想撤销 / 重做,运营想看操作日志,运维想异步执行。
- 错误做法:业务方法直接调,没有"操作"这个概念
- 正确做法:
- 把每个操作封装成命令对象:PayOrderCommand / CancelOrderCommand
- 命令包含所有执行信息(订单 ID / 用户 ID / …)
命令模式 = 把请求封装为对象,让请求可以排队、撤销、重做、记录日志。
1.2 命令模式的 3 个真实场景
大白商城里,命令模式用在:
| 订单操作 | 下单/支付/取消/退款 |
| 撤销重做 | 编辑器撤销/重做 |
| 异步任务 | 异步执行操作 |
| 宏命令 | 一组操作打包 |
| 事务封装 | 把操作封装成事务 |
任何"需要撤销/重做/排队/记录日志"的场景,都用命令模式。
二、反面教材:硬编码调用的"灾难现场"
我们看 4 个反面教材,看它们是怎么一步步崩的。
2.1 反面教材 v1:直接调方法
/**
* ❌ 反面教材 v1:直接调方法
*/
public void pay(String orderId) {
orderDao.updateStatus(orderId, "PAID");
// 无法撤销
}
翻车现场:
| ① | 无法撤销 | 操作不可回退 |
| ② | 无法记录 | 操作不可审计 |
2.2 反面教材 v2:撤销逻辑分散
/**
* ❌ 反面教材 v2:撤销逻辑分散
*/
public void cancel(String orderId) {
// 撤销支付
paymentService.refund(orderId);
// 撤销库存
stockService.increase(...);
// 撤销订单状态
orderDao.updateStatus(orderId, "CANCELLED");
// ❌ 撤销逻辑到处都是
}
翻车现场:
| ① | 撤销逻辑重复 | 改一处要改 N 处 |
| ② | 撤销不全 | 漏撤销某个步骤 |
2.3 反面教材 v3:历史记录分散
/**
* ❌ 反面教材 v3:历史记录分散
*/
public class OrderServiceV3 {
// 每个方法自己记日志
// 没有统一的操作栈
}
翻车现场:
| ① | 日志分散 | 难审计 |
| ② | 无法重做 | 操作丢失 |
2.4 反面教材 v4:完全没抽象
/**
* ❌ 反面教材 v4:完全没抽象
*/
public class OrderServiceV4 {
public void process() {
// 1000 行所有逻辑
}
}
翻车现场:
| ① | 无法复用 | 改一处牵动全身 |
| ② | 无法测试 | 单元测试跑不动 |
2.5 4 个反面教材的共同病根
| 可撤销 | ❌ 全部没有 |
| 可重做 | ❌ 全部没有 |
| 可记录 | ❌ 全部分散 |
| 可排队 | ❌ 全部无法 |
必须上命令模式。
三、模式原理:命令的"命令对象 + 调用者 + 接收者"
3.1 命令的 4 个核心概念
命令(Command):
- 封装请求的对象
- 包含 receiver + 参数
- 提供 execute() / undo() 方法
接收者(Receiver):
- 真正执行操作的对象
- 业务方(订单/支付/库存服务)
调用者(Invoker):
- 持有命令
- 触发命令执行
客户端(Client):
- 创建命令
- 设置给调用者
大白商城例子:
| 命令 | PayOrderCommand / CancelOrderCommand |
| 接收者 | OrderService / PaymentService |
| 调用者 | OrderController / CommandQueue |
| 客户端 | 用户 |
3.2 命令的 5 个核心角色
| 命令接口(Command) | 定义命令接口 | OrderCommand |
| 具体命令 A(Concrete Command A) | 实现某命令 | PayOrderCommand |
| 具体命令 B(Concrete Command B) | 实现某命令 | CancelOrderCommand |
| 调用者(Invoker) | 持有命令并执行 | CommandQueue |
| 接收者(Receiver) | 真正执行 | OrderService |
3.3 一张图看懂命令
OrderController(调用者)
│
│ invoke(command)
│
└── PayOrderCommand(命令)
│
│ execute()
│
└── OrderService(接收者)
│
└── updateStatus() / 减库存 / 支付
3.4 命令的"灵魂三问"
Q1:命令 vs 策略,区别是什么?
答:
- 命令:封装请求(含 receiver + action + 参数)
- 策略:封装算法(只含算法)
- 命令"做什么"包含"对谁做",策略只关心"怎么做"
- 命令支持撤销/重做,策略不支持
Q2:命令 vs 责任链,区别是什么?
答:
- 命令:单命令执行(一个命令一个动作)
- 责任链:多步骤处理(多个 handler 链式)
- 命令"封装一个完整操作",责任链"分步骤处理"
- 命令可撤销,责任链不可
Q3:命令模式的核心价值是什么?
答:
- 可撤销 / 重做(undo/redo 栈)
- 可排队(command queue)
- 可记录(command log)
- 可宏命令(macro command)
3.5 命令的 3 种实现方式
| 命令接口 | 经典命令 | 标准 |
| Runnable | JDK 函数式命令 | 简单 |
| Lambda | Java 8+ | 函数式 |
大白商城主推命令接口方式(支持撤销/重做)。
四、实战代码:大白商城订单命令完整实现
下面是大白商城生产环境在用的命令实现,全套代码可直接复制到 IDEA 跑。
4.1 项目环境与依赖
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.dabai.mall</groupId>
<artifactId>mall-design-pattern-34</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>mall-design-pattern-34</name>
<description>大白商城 – 设计模式 34 命令模式</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.2 命令接口:OrderCommand
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
/**
* ✅ 命令接口:订单命令
* <p>
* – execute() 执行命令
* – undo() 撤销命令
*
* @author 大白商城技术团队
*/
public interface OrderCommand {
/**
* ✅ 执行命令
*/
void execute();
/**
* ✅ 撤销命令
*/
void undo();
/**
* 命令名(用于日志)
*/
String getCommandName();
}
4.3 接收者:OrderReceiver
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* ✅ 接收者:订单服务
* <p>
* 真正执行订单操作
*
* @author 大白商城技术团队
*/
@Slf4j
@Service
public class OrderReceiver {
public void createOrder(Order order) {
log.info("【订单】创建, orderId={}", order.getOrderId());
}
public void payOrder(Order order) {
log.info("【订单】支付, orderId={}", order.getOrderId());
}
public void cancelOrder(Order order) {
log.info("【订单】取消, orderId={}", order.getOrderId());
}
public void shipOrder(Order order) {
log.info("【订单】发货, orderId={}", order.getOrderId());
}
}
4.4 4 个具体命令
CreateOrderCommand:
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import lombok.extern.slf4j.Slf4j;
/**
* ✅ 具体命令:创建订单
*/
@Slf4j
public class CreateOrderCommand implements OrderCommand {
private final OrderReceiver receiver;
private final Order order;
public CreateOrderCommand(OrderReceiver receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
log.info("【命令】执行: 创建订单, orderId={}", order.getOrderId());
receiver.createOrder(order);
}
@Override
public void undo() {
log.info("【命令】撤销: 取消订单, orderId={}", order.getOrderId());
receiver.cancelOrder(order);
}
@Override
public String getCommandName() {
return "CREATE_ORDER";
}
}
PayOrderCommand:
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import lombok.extern.slf4j.Slf4j;
/**
* ✅ 具体命令:支付订单
*/
@Slf4j
public class PayOrderCommand implements OrderCommand {
private final OrderReceiver receiver;
private final Order order;
public PayOrderCommand(OrderReceiver receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
log.info("【命令】执行: 支付订单, orderId={}", order.getOrderId());
receiver.payOrder(order);
}
@Override
public void undo() {
log.info("【命令】撤销: 取消支付(退款), orderId={}", order.getOrderId());
receiver.cancelOrder(order);
}
@Override
public String getCommandName() {
return "PAY_ORDER";
}
}
CancelOrderCommand:
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import lombok.extern.slf4j.Slf4j;
/**
* ✅ 具体命令:取消订单
*/
@Slf4j
public class CancelOrderCommand implements OrderCommand {
private final OrderReceiver receiver;
private final Order order;
public CancelOrderCommand(OrderReceiver receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
log.info("【命令】执行: 取消订单, orderId={}", order.getOrderId());
receiver.cancelOrder(order);
}
@Override
public void undo() {
log.info("【命令】撤销: 恢复订单, orderId={}", order.getOrderId());
receiver.createOrder(order);
}
@Override
public String getCommandName() {
return "CANCEL_ORDER";
}
}
ShipOrderCommand:
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import lombok.extern.slf4j.Slf4j;
/**
* ✅ 具体命令:发货
*/
@Slf4j
public class ShipOrderCommand implements OrderCommand {
private final OrderReceiver receiver;
private final Order order;
public ShipOrderCommand(OrderReceiver receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
log.info("【命令】执行: 发货, orderId={}", order.getOrderId());
receiver.shipOrder(order);
}
@Override
public void undo() {
log.info("【命令】撤销: 召回发货, orderId={}", order.getOrderId());
// 实际召回
}
@Override
public String getCommandName() {
return "SHIP_ORDER";
}
}
配套 Order DTO:
package com.dabai.mall.order;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private String orderId;
private String userId;
private BigDecimal amount;
private String status;
}
4.5 调用者:CommandInvoker(含撤销/重做栈)
package com.dabai.mall.command;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Stack;
/**
* ✅ 调用者:命令执行器
* <p>
* 包含 undo / redo 栈
*
* @author 大白商城技术团队
*/
@Slf4j
@Service
public class CommandInvoker {
/**
* ✅ 已执行命令栈(用于撤销)
*/
private final Stack<OrderCommand> undoStack = new Stack<>();
/**
* ✅ 已撤销命令栈(用于重做)
*/
private final Stack<OrderCommand> redoStack = new Stack<>();
/**
* ✅ 执行命令
*/
public void execute(OrderCommand command) {
command.execute();
undoStack.push(command);
// 执行后清空 redo 栈
redoStack.clear();
}
/**
* ✅ 撤销
*/
public void undo() {
if (undoStack.isEmpty()) {
log.warn("【命令】无可撤销命令");
return;
}
OrderCommand command = undoStack.pop();
command.undo();
redoStack.push(command);
}
/**
* ✅ 重做
*/
public void redo() {
if (redoStack.isEmpty()) {
log.warn("【命令】无可重做命令");
return;
}
OrderCommand command = redoStack.pop();
command.execute();
undoStack.push(command);
}
}
4.6 业务调用方
package com.dabai.mall.controller;
import com.dabai.mall.command.*;
import com.dabai.mall.order.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* ✅ 订单 Controller:命令模式的调用方
* <p>
* 业务方只调 CommandInvoker,不关心命令细节
*
* @author 大白商城技术团队
*/
@RestController
@RequestMapping("/api/order")
public class OrderController {
@Autowired
private OrderReceiver receiver;
@Autowired
private CommandInvoker invoker;
@PostMapping("/create")
public String create(@RequestBody Order order) {
// ✅ 创建命令
OrderCommand command = new CreateOrderCommand(receiver, order);
// ✅ 执行
invoker.execute(command);
return "success";
}
@PostMapping("/pay")
public String pay(@RequestBody Order order) {
OrderCommand command = new PayOrderCommand(receiver, order);
invoker.execute(command);
return "success";
}
@PostMapping("/cancel")
public String cancel(@RequestBody Order order) {
OrderCommand command = new CancelOrderCommand(receiver, order);
invoker.execute(command);
return "success";
}
@PostMapping("/ship")
public String ship(@RequestBody Order order) {
OrderCommand command = new ShipOrderCommand(receiver, order);
invoker.execute(command);
return "success";
}
@PostMapping("/undo")
public String undo() {
invoker.undo();
return "success";
}
@PostMapping("/redo")
public String redo() {
invoker.redo();
return "success";
}
}
4.7 宏命令:组合多个命令
/**
* ✅ 宏命令:组合多个命令
*/
public class MacroCommand implements OrderCommand {
private final List<OrderCommand> commands = new ArrayList<>();
public void addCommand(OrderCommand command) {
commands.add(command);
}
@Override
public void execute() {
log.info("【宏命令】开始, 包含 {} 个命令", commands.size());
for (OrderCommand command : commands) {
command.execute();
}
}
@Override
public void undo() {
// ✅ 倒序撤销
for (int i = commands.size() – 1; i >= 0; i—) {
commands.get(i).undo();
}
}
@Override
public String getCommandName() {
return "MACRO";
}
}
4.8 单元测试
package com.dabai.mall.command;
import com.dabai.mall.order.Order;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.*;
/**
* 命令模式完整单元测试
*/
class OrderCommandTest {
private OrderReceiver receiver;
private CommandInvoker invoker;
@BeforeEach
void setUp() {
receiver = new OrderReceiver();
invoker = new CommandInvoker();
}
@Test
void testCreateOrder() {
Order order = Order.builder().orderId("ORDER_001").build();
OrderCommand command = new CreateOrderCommand(receiver, order);
invoker.execute(command);
}
@Test
void testPayOrder() {
Order order = Order.builder().orderId("ORDER_001").build();
OrderCommand command = new PayOrderCommand(receiver, order);
invoker.execute(command);
}
@Test
void testCancelOrder() {
Order order = Order.builder().orderId("ORDER_001").build();
OrderCommand command = new CancelOrderCommand(receiver, order);
invoker.execute(command);
}
@Test
void testUndo() {
Order order = Order.builder().orderId("ORDER_001").build();
OrderCommand command = new CreateOrderCommand(receiver, order);
invoker.execute(command);
invoker.undo();
}
@Test
void testRedo() {
Order order = Order.builder().orderId("ORDER_001").build();
OrderCommand command = new CreateOrderCommand(receiver, order);
invoker.execute(command);
invoker.undo();
invoker.redo();
}
@Test
void testUndoEmptyStack() {
// ✅ 空栈撤销不报错
invoker.undo();
}
@Test
void testRedoEmptyStack() {
// ✅ 空栈重做不报错
invoker.redo();
}
@Test
void testMacroCommand() {
Order order = Order.builder().orderId("ORDER_001").build();
MacroCommand macro = new MacroCommand();
macro.addCommand(new CreateOrderCommand(receiver, order));
macro.addCommand(new PayOrderCommand(receiver, order));
macro.addCommand(new ShipOrderCommand(receiver, order));
invoker.execute(macro);
invoker.undo(); // 全部撤销
}
@Test
void testUndoClearRedo() {
// ✅ 执行新命令后 redo 栈被清空
Order order = Order.builder().orderId("ORDER_001").build();
invoker.execute(new CreateOrderCommand(receiver, order));
invoker.execute(new PayOrderCommand(receiver, order));
invoker.undo();
invoker.undo();
invoker.redo(); // ✅ 可重做
invoker.execute(new CancelOrderCommand(receiver, order)); // ✅ 新命令
invoker.redo(); // ❌ redo 栈已清空
}
}
五、命令 vs 策略 / 责任链
5.1 一张表看清区别
| 命令 | 封装请求 | ✅ 支持 | 撤销/重做/队列 |
| 策略 | 封装算法 | ❌ | 多种算法 |
| 责任链 | 链式处理 | ❌ | 链式处理 |
5.2 一个具体例子区分 3 个模式
大白商城:
| 命令 | 订单撤销 / 重做 |
| 策略 | 支付 4 种方式 |
| 责任链 | 风控 4 步流程 |
5.3 决策树
需要撤销/重做/队列?
├── 是
│ └── ✅ 命令模式
└── 否
├── 多种算法可切换?
│ ├── 是 → ✅ 策略
│ └── 否
│ ├── 链式处理?
│ │ ├── 是 → ✅ 责任链
│ │ └── 否 → 普通类
5.4 命令 vs 策略:易混淆
| 关注 | 请求 | 算法 |
| 撤销 | ✅ | ❌ |
| 队列 | ✅ | ❌ |
| 接收者 | 显式 | 不需要 |
大白商城选型:
| 订单撤销 | 命令 | 需要撤销/重做 |
| 支付方式 | 策略 | 多种算法 |
| 风控流程 | 责任链 | 链式处理 |
| 异步任务 | 命令 | 排队执行 |
六、JDK 的 Runnable 怎么用命令?
6.1 Runnable:函数式命令
源码位置:
- OpenJDK 17
- src/java.base/share/classes/java/lang/Runnable.java
核心源码:
/**
* ✅ 命令接口:可执行任务
*/
@FunctionalInterface
public interface Runnable {
/**
* ✅ execute 方法
*/
void run();
}
关键点:
| run() | 命令执行方法 |
| @FunctionalInterface | Lambda 友好 |
| 无参数无返回值 | 简化 |
6.2 用 Runnable 作为命令
/**
* ✅ Runnable 作为命令
*/
Runnable command = () -> {
log.info("执行命令");
};
new Thread(command).start(); // 异步执行命令
6.3 Callable:带返回值的命令
/**
* ✅ Callable:带返回值的命令
*/
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
6.4 大白商城的"抄作业"
/**
* 借鉴 Runnable:异步命令
*/
public class AsyncCommandExecutor {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public void executeAsync(Runnable command) {
executor.execute(command);
}
}
七、Spring @Transactional 怎么用命令?
7.1 @Transactional:事务命令
Spring 的 @Transactional 注解用命令模式封装事务:
/**
* ✅ @Transactional:事务命令
*/
@Service
public class OrderService {
@Transactional
public void payOrder(String orderId) {
// 1️⃣ 命令执行
orderDao.updateStatus(orderId, "PAID");
stockService.decrease(...);
// 任何一个失败,整个命令撤销(回滚)
}
}
关键点:
| @Transactional | 事务命令标记 |
| 整体执行 | 命令执行 |
| 失败回滚 | 命令撤销 |
7.2 TransactionTemplate:命令式事务
/**
* ✅ TransactionTemplate:命令式事务
*/
@Service
public class OrderTxService {
@Autowired
private TransactionTemplate transactionTemplate;
public void payOrder(String orderId) {
// ✅ 命令式事务
transactionTemplate.execute(status -> {
// 1️⃣ 命令执行
orderDao.updateStatus(orderId, "PAID");
stockService.decrease(...);
return null;
});
}
}
7.3 大白商城的"抄作业"
/**
* 借鉴 @Transactional:命令式事务
*/
public class CommandExecutor {
/**
* ✅ 通用命令执行(含 try-catch)
*/
public static <T> T execute(Command<T> command) {
try {
return command.execute();
} catch (Exception e) {
log.error("命令执行失败: {}", command.getName(), e);
throw e;
}
}
}
@FunctionalInterface
public interface Command<T> {
T execute();
default String getName() { return "unnamed"; }
}
八、工程决策 Checklist
8.1 ✅ 这 5 种情况,强烈建议用命令模式
| ① | 需要撤销 / 重做 | 编辑器/订单 |
| ② | 需要操作队列 | 异步任务 |
| ③ | 需要操作日志 | 审计 |
| ④ | 需要事务封装 | @Transactional |
| ⑤ | 需要宏命令 | 一组操作 |
8.2 ❌ 这 5 种情况,绝对不要用命令模式
| ① | 业务简单 | 过度设计 |
| ② | 不需要撤销 | 复杂度浪费 |
| ③ | 单一操作 | 直接调 |
| ④ | 追求简单 | 类多 |
| ⑤ | 业务经常变 | 难维护 |
8.3 ⚠️ 命令的 6 大常见坑
| ① | 撤销不全 | 撤销不彻底 | 完整 undo |
| ② | redo 栈混乱 | 顺序错乱 | 清空栈 |
| ③ | 状态不一致 | 撤销后状态错 | 状态机 |
| ④ | 命令对象多 | 类爆炸 | 抽象公共 |
| ⑤ | receiver 缺失 | 业务无法执行 | 显式 receiver |
| ⑥ | 并发问题 | 栈不安全 | 同步 |
8.4 面试官视角:命令高频追问
Q1:命令 vs 策略,区别是什么?
答:命令封装请求(含 receiver + action + 参数),策略封装算法。命令支持撤销/重做,策略不支持。
Q2:命令模式怎么实现撤销/重做?
答:维护两个栈(undo / redo)。execute 时 push undo 栈 + clear redo 栈;undo 时从 undo 栈 pop + push redo 栈;redo 时从 redo 栈 pop + push undo 栈。
Q3:Spring @Transactional 是命令模式吗?
答:严格说不是经典命令模式,但用命令思想——把一组操作封装成一个"事务命令",整体执行,失败回滚(撤销)。
九、与其他模式协作
9.1 命令 + 撤销/重做 = 撤销重做栈
9.2 命令 + 宏命令 = 一组命令
9.3 命令 + 状态 = 命令改变状态
/**
* ✅ 命令 + 状态:命令执行后改状态
*/
public class PayOrderCommand implements OrderCommand {
@Override
public void execute() {
receiver.payOrder(order);
// ✅ 改状态
order.setState(new PaidState());
}
}
9.4 大白商城模式协作全景图
┌──────────────┐
│ 命令 │ ← 本篇
└──────┬───────┘
│
┌───────────┬───────┼───────┬───────────┐
│ │ │ │ │
┌───▼───┐ ┌────▼───┐ ┌▼────┐ ┌▼─────┐ ┌───▼────┐
│策略 │ │状态 │ │单例 │ │模板 │ │ 责任链 │
│(算法) │ │(变化) │ │(命令)│ │方法 │ │ (链式) │
└───────┘ └────────┘ └──────┘ └──────┘ └───────┘
26 篇 32 篇 02 篇 28 篇 46 篇
十、本篇小结 + 下篇预告
10.1 本篇小结(5 个核心要点)
10.2 一句话总结
命令不是"换种方式调用方法",是"把操作封装成对象,让操作可撤销/可重做/可排队"。大白商城从直接调方法 → 4 个 Command 类 + 撤销/重做栈——用户操作可撤销,运营日志可追溯,运维任务可异步。
10.3 知识脑图
命令模式
├── 4 个核心概念
│ ├── 命令(Command)
│ ├── 接收者(Receiver)
│ ├── 调用者(Invoker)
│ └── 客户端(Client)
├── 5 大角色
│ ├── 命令接口(OrderCommand)
│ ├── 具体命令(4 个)
│ ├── 调用者(CommandInvoker)
│ ├── 接收者(OrderReceiver)
│ └── 客户端(OrderController)
├── 4 个具体命令
│ ├── CreateOrderCommand
│ ├── PayOrderCommand
│ ├── CancelOrderCommand
│ └── ShipOrderCommand
├── 1 个宏命令
│ └── MacroCommand
├── 模式对比
│ ├── vs 策略(撤销 vs 算法)
│ ├── vs 责任链(单命令 vs 链式)
│ └── vs 观察者(主动 vs 被动)
├── JDK Runnable
│ ├── Runnable(无返回值)
│ ├── Callable(有返回值)
│ └── Executor(执行)
└── Spring @Transactional
├── 注解式事务
├── TransactionTemplate
└── 命令式事务
10.4 下篇预告
第 35 篇【命令模式 – 源码剖析篇】:JDK / Spring 中的命令实现
下一篇我们会深入源码,回答三个问题:
并附完整的源码解读 + 流程图 + 大白商城的"抄作业"实践。
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

