设计模式 从零到生产级掌握
设计模式最容易学偏:背了一堆名字,写代码时不是不会用,就是到处乱用。真正的掌握标准不是“能说出定义”,而是能判断:问题的变化点在哪里,不用模式会怎样,用了模式解决了什么,又增加了什么成本。
一句话建立主线:
设计模式是隔离变化点的经验集合。它不是为了让代码看起来复杂,而是让真实会变化的部分更容易扩展,让稳定的主流程更少被修改。
学习目标
学完这一页,你要能做到:
- 解释创建型、结构型、行为型模式分别解决什么问题。
- 判断一个场景是否真的需要设计模式。
- 解释不用模式时会出现什么维护问题。
- 解释使用模式后调用链怎么变化。
- 解释每个模式的代价和误用后果。
- 能把模式落到订单、支付、通知、采集、导入、权限、审批、第三方接口等商业场景。
- 能结合 Spring、JDK 和项目代码解释模式,而不是只背定义。
- 能在面试中先讲业务问题,再讲模式解决方式,最后讲边界。
如果要按“零基础能看懂、原理能讲清、项目能落地、面试能回答、线上能排查”的标准验收,请配合阅读 设计模式从零到精通验收清单。本页负责建立主线,验收清单负责把创建型、结构型、行为型模式按变化点、Demo、商业场景、误用后果和排查逐项拆开。
先理解变化点
设计模式不是从“我要用哪个模式”开始,而是从“哪里会变”开始。
flowchart TD
A["阅读业务需求"] --> B["找变化点"]
B --> C{"变化是否真实存在"}
C -- "否" --> D["先直接写清楚"]
C -- "是" --> E{"变化是否会持续发生"}
E -- "否" --> D
E -- "是" --> F["选择合适模式隔离变化"]
F --> G["保留清晰调用链和测试"]常见变化点:
| 变化点 | 常见模式 | 示例 |
|---|---|---|
| 创建哪种对象会变 | 工厂 | 支付渠道、消息发送器 |
| 对象参数很多 | 建造者 | 查询条件、导出配置 |
| 全局唯一资源 | 单例 | 配置、线程池、连接池 |
| 调用前后要增强 | 代理 | 事务、权限、日志 |
| 原接口不兼容 | 适配器 | 第三方医院接口 |
| 能力要叠加 | 装饰器 | IO 流、数据脱敏包装 |
| 多种规则切换 | 策略 | 折扣、计费、文件解析 |
| 一件事通知多方 | 观察者 | 本地事件、领域事件 |
| 流程固定步骤可变 | 模板方法 | 数据导入、文件处理 |
| 多步骤顺序处理 | 责任链 | 校验链、过滤器链 |
三大分类怎么理解
flowchart TD
A["设计模式"] --> B["创建型"]
A --> C["结构型"]
A --> D["行为型"]
B --> E["对象怎么创建"]
C --> F["对象怎么组合或增强"]
D --> G["对象之间怎么协作"]| 分类 | 解决的问题 | 代表模式 |
|---|---|---|
| 创建型 | 对象创建逻辑复杂或会变化 | 单例、工厂、建造者 |
| 结构型 | 类或对象组合关系需要调整 | 代理、装饰器、适配器 |
| 行为型 | 多个对象之间的流程和规则协作 | 策略、观察者、模板方法、责任链 |
创建型模式
单例模式
适合全局唯一资源,例如线程池、配置中心客户端、连接池管理器。
不使用会怎样:
- 多处重复创建昂贵对象。
- 状态不一致。
- 资源无法统一关闭和监控。
Spring 中大多数 Bean 默认是单例,但这是容器级单例,不等于手写 JVM 全局单例。
public class IdGenerator {
private static final IdGenerator INSTANCE = new IdGenerator();
private IdGenerator() {
}
public static IdGenerator getInstance() {
return INSTANCE;
}
public long nextId() {
return System.currentTimeMillis();
}
}误用后果:把有用户状态、请求状态的对象做成单例,会产生线程安全和数据串扰问题。
工厂模式
适合“创建哪种实现”会变化的场景。
flowchart TD
A["业务传入 type"] --> B["Factory"]
B --> C["AliPayService"]
B --> D["WechatPayService"]
B --> E["UnionPayService"]支付工厂 Demo:
public interface PayService {
String channel();
void pay(PayCommand command);
}
@Component
public class PayFactory {
private final Map<String, PayService> serviceMap;
public PayFactory(List<PayService> services) {
this.serviceMap = services.stream()
.collect(Collectors.toMap(PayService::channel, service -> service));
}
public PayService get(String channel) {
PayService service = serviceMap.get(channel);
if (service == null) {
throw new IllegalArgumentException("不支持的支付渠道");
}
return service;
}
}不使用会怎样:到处 if ("ALI") new AliPayService(),新增渠道要改很多地方。
建造者模式
适合参数很多、可选项很多、构建过程需要校验的对象。
OrderQuery query = OrderQuery.builder()
.status(1)
.startTime(start)
.endTime(end)
.pageNo(1)
.pageSize(20)
.build();不使用会怎样:构造器参数很长,调用者容易传错顺序;对象创建规则散落在各处。
误用后果:只有两三个简单字段也强行 Builder,会让代码变啰嗦。
结构型模式
代理模式
代理强调“控制访问或统一增强”。
flowchart TD
A["调用方"] --> B["代理对象"]
B --> C["开启事务/权限/日志"]
C --> D["目标对象"]
D --> E["提交或回滚/记录结果"]Spring 事务就是典型代理:
- 外部调用进入代理对象。
- 代理开启事务。
- 调用目标方法。
- 方法成功提交,异常回滚。
为什么同类自调用事务失效:因为没有经过代理对象。
装饰器模式
装饰器强调“不改原类,动态叠加能力”。
Java IO:
InputStream input = new BufferedInputStream(
new FileInputStream("data.txt")
);FileInputStream 提供文件读取能力,BufferedInputStream 叠加缓冲能力。它们关注的是能力叠加,不是访问控制。
适配器模式
适配器解决接口不兼容。
flowchart TD
A["统一业务接口"] --> B["医院 A 适配器"]
A --> C["医院 B 适配器"]
B --> D["医院 A 原始接口"]
C --> E["医院 B 原始接口"]医疗平台对接不同医院时很常见:
public interface PatientClient {
PatientDTO getPatient(String patientId);
}
@Component
public class HospitalAAdapter implements PatientClient {
private final HospitalAApi api;
public PatientDTO getPatient(String patientId) {
HospitalAResponse response = api.query(patientId);
return new PatientDTO(response.getPid(), response.getName());
}
}适配器里不要写复杂业务规则。它主要做协议、字段、错误码转换。
行为型模式
策略模式
策略解决“同一件事有多种做法”。
flowchart TD
A["下单"] --> B["选择优惠策略"]
B --> C["会员折扣"]
B --> D["满减"]
B --> E["新人券"]策略模式不是为了消灭所有 if。只有规则会持续扩展时才值得拆。
public interface DiscountStrategy {
String type();
BigDecimal discount(BigDecimal amount);
}观察者模式
观察者适合进程内事件通知。
flowchart TD
A["订单创建成功"] --> B["发布本地事件"]
B --> C["发站内信"]
B --> D["写操作日志"]
B --> E["刷新本地缓存"]注意:观察者不等于 MQ。跨服务可靠通知、削峰、重试、持久化,应该用消息队列。
模板方法模式
模板方法固定流程骨架,把变化步骤交给子类。
public abstract class ImportTemplate {
public final void importData(File file) {
checkFile(file);
List<String> rows = readRows(file);
List<Object> data = parse(rows);
validate(data);
save(data);
}
protected abstract List<Object> parse(List<String> rows);
protected abstract void save(List<Object> data);
}适合数据导入、文件解析、报表生成这类流程顺序稳定的场景。
责任链模式
责任链适合多个处理器按顺序处理请求。
flowchart TD
A["创建订单请求"] --> B["参数校验"]
B --> C["库存校验"]
C --> D["价格校验"]
D --> E["风控校验"]
E --> F["创建订单"]Demo:
public interface OrderCheckHandler {
void check(CreateOrderCommand command);
}
@Service
public class OrderCreateService {
private final List<OrderCheckHandler> handlers;
public void create(CreateOrderCommand command) {
for (OrderCheckHandler handler : handlers) {
handler.check(command);
}
saveOrder(command);
}
}链路过长时必须有日志,否则线上排查会很痛苦。
商业项目组合
| 场景 | 推荐组合 | 原因 |
|---|---|---|
| 支付渠道 | 工厂 + 策略 | 工厂找渠道,策略执行支付 |
| 订单创建 | 责任链 + 策略 | 校验链有顺序,不同优惠策略可替换 |
| 医院接口接入 | 适配器 + 策略 | 适配不同协议,按医院选择实现 |
| 数据导入 | 模板方法 + 责任链 | 流程固定,校验可插拔 |
| 权限和事务 | 代理 | 统一增强目标方法 |
| 本地业务事件 | 观察者 | 一件事发生后通知多个本地处理器 |
| 文件流处理 | 装饰器 | 动态叠加缓冲、压缩、加密能力 |
过度设计怎么识别
flowchart TD
A["想引入模式"] --> B{"是否有多个实现"}
B -- "否" --> C{"短期是否确定会扩展"}
C -- "否" --> D["不要急着抽象"]
B -- "是" --> E{"抽象是否减少修改面"}
E -- "是" --> F["可以引入"]
E -- "否" --> D过度设计表现:
- 一个实现也抽接口、工厂、策略。
- 需求还不稳定就抽象。
- 为了模式拆出大量小类,调用链难追。
- 所有业务事件化,排查不知道谁处理了。
- 模式没有减少修改面,只是增加文件数量。
好的设计应该让新人更容易理解变化点,而不是迷路。
面试标准回答
怎么理解设计模式
设计模式是常见设计问题的经验总结,核心是识别变化点并隔离变化。它不是为了套 UML 或让代码复杂,而是在变化真实存在、扩展会持续发生时,让新增功能少改老代码。同时也要避免过度设计,简单稳定的业务直接写清楚更好。工厂和策略怎么配合
工厂负责根据类型找到具体策略对象,策略负责执行业务逻辑。比如支付场景中,工厂根据支付渠道找到支付宝、微信或银联策略,业务层只依赖统一 PayService 接口,新增渠道时新增实现即可,尽量少改原有流程。代理和装饰器区别
代理和装饰器结构上都可能包一层对象,但意图不同。代理强调访问控制或横切增强,比如事务、权限、远程代理;装饰器强调能力叠加,比如 Java IO 中给输入流增加缓冲、压缩或加密能力。责任链适合什么场景
责任链适合一组有顺序、职责单一、可插拔的处理步骤,比如订单创建校验、网关过滤器、Spring Security 过滤器链。它能让每个处理器独立扩展,但链路过长时要有顺序管理、异常策略和日志,否则排查困难。关联知识点
| 模式 | 继续学习 |
|---|---|
| 从零到精通验收 | 设计模式从零到精通验收清单 |
| 商业场景训练营 | 设计模式商业场景训练营 |
| 单例 | 单例模式 |
| 工厂 | 工厂模式 |
| 建造者 | 建造者模式 |
| 代理 | 代理模式 |
| 装饰器 | 装饰器模式 |
| 适配器 | 适配器模式 |
| 策略 | 策略模式 |
| 观察者 | 观察者模式 |
| 模板方法 | 模板方法模式 |
| 责任链 | 责任链模式 |
| 面试题 | 设计模式面试题 |
本章小结
设计模式的最终目标不是“代码里用了多少模式”,而是“变化来了是否容易改”。真正生产级的设计模式能力,是能看到业务变化点,能克制不必要抽象,也能在支付、订单、采集、导入、审批、权限、第三方接口这些真实场景中选择合适的模式。
