Update 设计模式 - 目录.md

补充项目中的用法(下单流程中用到的几种模式)和附录 A1,较少用到的模式标为选读。
This commit is contained in:
CyC2018
2026-10-29 04:55:49 +08:00
parent d22e28d653
commit 39a1bfebfd
+92 -13
View File
@@ -16,6 +16,66 @@ JDK 中,Runtime.getRuntime() 是单例,Calendar.getInstance() 是静态工
Spring 中的用法见 [Spring 中的设计模式](Spring.md#七spring-中的设计模式)。
后面的目录中,标为“选读”的模式在面试中很少单独问到,了解意图即可。
### 项目中的用法
设计模式针对的是变化:某处代码会随着需求不断增加分支或步骤时,用模式把变化的部分隔离出来,以后只需新增代码而不必修改已有的代码。以电商的下单流程为例,几个常用模式各自用在:
| 环节 | 变化点 | 模式 |
| --- | --- | --- |
| 下单前的校验 | 校验项不断增加:参数、限购、黑名单、库存 | [责任链](设计模式%20-%20责任链.md) |
| 计算运费、选择支付渠道 | 同一件事有多种算法,按类型选择其中一种 | [策略](设计模式%20-%20策略.md)加[简单工厂](设计模式%20-%20简单工厂.md) |
| 接收多家物流公司的回调 | 步骤固定(验签、幂等、处理、应答),只有处理的部分不同 | [模板方法](设计模式%20-%20模板方法.md) |
| 支付成功后加积分、发短信 | 接收方不断增加,发布方不想知道有哪些接收方 | [观察者](设计模式%20-%20观察者.md) |
| 事务、分布式锁、日志 | 与业务无关、很多方法都要加的逻辑 | [代理](设计模式%20-%20代理.md),即 Spring AOP |
以下单前的校验为例,每个校验项是一个处理者,按顺序执行,任何一个不通过就抛出异常,后面的不再执行:
```java
public interface OrderValidator {
void check(OrderRequest req); // 不通过时抛出异常,链条到此为止
}
@Component @Order(1)
public class ParamValidator implements OrderValidator {
public void check(OrderRequest req) {
if (req.count() <= 0) throw new IllegalArgumentException("数量必须大于 0");
}
}
@Component @Order(2)
public class LimitValidator implements OrderValidator {
public void check(OrderRequest req) {
if (req.count() > 2) throw new IllegalStateException("每人限购 2 件");
}
}
@Service
public class OrderService {
private final List<OrderValidator> validators; // Spring 按 @Order 排好序注入
public OrderService(List<OrderValidator> validators) {
this.validators = validators;
}
public void create(OrderRequest req) {
validators.forEach(v -> v.check(req));
// 校验全部通过,创建订单
}
}
```
再加上库存校验(@Order(3)),购买 3 件时依次执行参数校验、限购校验,抛出“每人限购 2 件”,库存校验不再执行(Spring Boot 3.3 实测,见[附录 A1](#a1-下单校验的责任链))。新增黑名单校验只需要再写一个带 @Component 的实现类,OrderService 不用修改。
几个容易混淆的模式:
- 策略和工厂常一起出现:工厂决定取得哪一个对象,策略决定执行哪一种算法,见[与简单工厂和 Spring 容器结合](设计模式%20-%20策略.md#2-与简单工厂和-spring-容器结合);
- 工厂方法一次创建一个对象,由子类决定创建哪一种;抽象工厂创建一组必须一起使用的相关对象,见[抽象工厂](设计模式%20-%20抽象工厂.md);
- 责任链中的请求依次经过多个处理者,策略只选择其中一个算法执行。
需要注意的是,模式不是用得越多越好。只有两三个分支、短期内不会再增加的 if-else 不需要改成策略,引入模式的理由应该是已经出现或可以预见的变化。
## 二、创建型
- [单例.md](设计模式%20%20-%20单例.md)
@@ -23,33 +83,51 @@ Spring 中的用法见 [Spring 中的设计模式](Spring.md#七spring-中的设
- [工厂方法.md](设计模式%20-%20工厂方法.md)
- [抽象工厂.md](设计模式%20-%20抽象工厂.md)
- [生成器.md](设计模式%20-%20生成器.md)
- [原型模式.md](设计模式%20-%20原型模式.md)
- [原型模式.md](设计模式%20-%20原型模式.md)(选读)
## 三、行为型
- [责任链.md](设计模式%20-%20责任链.md)
- [命令.md](设计模式%20-%20命令.md)
- [解释器.md](设计模式%20-%20解释器.md)
- [迭代器.md](设计模式%20-%20迭代器.md)
- [中介者.md](设计模式%20-%20中介者.md)
- [备忘录.md](设计模式%20-%20备忘录.md)
- [命令.md](设计模式%20-%20命令.md)(选读)
- [解释器.md](设计模式%20-%20解释器.md)(选读)
- [迭代器.md](设计模式%20-%20迭代器.md)(选读)
- [中介者.md](设计模式%20-%20中介者.md)(选读)
- [备忘录.md](设计模式%20-%20备忘录.md)(选读)
- [观察者.md](设计模式%20-%20观察者.md)
- [状态.md](设计模式%20-%20状态.md)
- [状态.md](设计模式%20-%20状态.md)(选读)
- [策略.md](设计模式%20-%20策略.md)
- [模板方法.md](设计模式%20-%20模板方法.md)
- [访问者.md](设计模式%20-%20访问者.md)
- [空对象.md](设计模式%20-%20空对象.md)
- [访问者.md](设计模式%20-%20访问者.md)(选读)
- [空对象.md](设计模式%20-%20空对象.md)(选读)
## 四、结构型
- [适配器.md](设计模式%20-%20适配器.md)
- [桥接.md](设计模式%20-%20桥接.md)
- [组合.md](设计模式%20-%20组合.md)
- [桥接.md](设计模式%20-%20桥接.md)(选读)
- [组合.md](设计模式%20-%20组合.md)(选读)
- [装饰.md](设计模式%20-%20装饰.md)
- [外观.md](设计模式%20-%20外观.md)
- [享元.md](设计模式%20-%20享元.md)
- [外观.md](设计模式%20-%20外观.md)(选读)
- [享元.md](设计模式%20-%20享元.md)(选读)
- [代理.md](设计模式%20-%20代理.md)
## 附录:实测
### A1 下单校验的责任链
Spring Boot 3.3.5(Spring Framework 6.1.14)、JDK 21,Apple M1 Pro。三个校验器与正文相同,库存校验 StockValidator 标注 @Order(3)、只打印一行;每个校验器先打印自己的名称。用 @Import 注册时故意按库存、参数、限购的顺序列出,注入的 List 仍按 @Order 排序。依次以购买 1 件和 3 件调用 create():
```text
== 购买 1 件
参数校验
限购校验
库存校验
创建订单
== 购买 3 件
参数校验
限购校验
下单失败:每人限购 2 件
```
## 参考资料
- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
@@ -58,3 +136,4 @@ Spring 中的用法见 [Spring 中的设计模式](Spring.md#七spring-中的设
- [Design Patterns](http://www.oodesign.com/)
- [Design patterns implemented in Java](http://java-design-patterns.com/)
- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html)
- [Spring Framework: Using @Autowired](https://docs.spring.io/spring-framework/reference/core/beans/annotation-config/autowired.html)