Skip to content

设计模式 速查卡

🎯 覆盖 18 题 | ⭐ 高频 9 题 | 预计扫描 9 分钟 📌 先看⭐一句话答案 → 展开要点 → 自测清单检验


一、基础与原则

知识地图:SOLID 六原则 → 23 种 GoF 模式(创建型 / 结构型 / 行为型)→ Spring 中的设计模式

⭐ SOLID 六大原则

一句话: SRP 单一职责 / OCP 开闭 / LSP 里氏替换 / ISP 接口隔离 / DIP 依赖倒置 + 迪米特法则——核心三句话:封装变化、多用组合少用继承、面向接口编程。

原则一句话示例
SRP 单一职责一个类只负责一件事Controller / Service / DAO 分层
OCP 开闭对扩展开放,对修改关闭策略模式新增策略不改调用方
LSP 里氏替换子类能替换父类且行为正确List list = new ArrayList()
ISP 接口隔离接口小而专,不强迫依赖UserQueryService / UserCommandService 拆分
DIP 依赖倒置依赖抽象不依赖实现@Autowired 注入接口类型
LoD 迪米特只与直接朋友通信Facade 隐藏子系统复杂性

⭐ Spring 中的设计模式

一句话: Spring 是模式集大成者——工厂(BeanFactory) / 代理(AOP) / 模板方法(JdbcTemplate) / 观察者(Event) 是面试必讲四个,再加单例 / 策略 / 适配器。

模式Spring 体现关键词
工厂BeanFactory / ApplicationContextIoC 容器本身就是工厂
单例Bean 默认 singleton容器保证唯一
代理AOP(JDK / CGLIB)@Transactional / @Async
模板方法JdbcTemplate / RedisTemplate骨架固定,用户传回调
观察者ApplicationEvent + @EventListener发布-订阅
策略Resource 接口(ClassPath/Url/FileSystem)按前缀选策略
适配器HandlerAdapter(MVC)统一调用不同 Controller
责任链Spring Security Filter 链逐个 Filter 放行

二、创建型模式

⭐ 单例四种写法 + DCL 为什么要 volatile

一句话: 四种写法各有取舍;DCL 必须 volatile 是因为 new 分三步可能指令重排,导致其他线程拿到未初始化对象。

写法线程安全懒加载防反射推荐度
饿汉式是(JVM 类加载)★★★
DCL是(需 volatile)★★★★
静态内部类是(内部类加载)★★★★★
枚举是(JVM 保证)★★★★★

DCL volatile 原理:

new Singleton() 三步:①分配内存 → ②初始化对象 → ③引用指向内存
JVM 可能重排为 ①→③→② → 线程B看到非null但未初始化的对象 → NPE
volatile 内存屏障禁止重排,保证 ①→②→③

⚠️ 易错:枚举是 Effective Java 推荐的终极写法,天然防反射 + 反序列化破坏


⭐ 三种工厂对比

一句话: 简单工厂一个类集中创建(违反 OCP);工厂方法一产品一工厂(OCP);抽象工厂一族一工厂(产品族一致性)。

维度简单工厂工厂方法抽象工厂
工厂数量1 个多个(一产品一工厂)多个(一族一工厂)
产品维度多种产品单一产品产品族
新增产品改工厂代码新增工厂子类改所有工厂(难)
新增产品族新增工厂子类(易)
开闭原则违反遵守部分遵守
Spring 例BeanFactory.getBean(name)FactoryBean 接口

💡 工厂方法是抽象工厂的特例——产品族只有一种产品时退化为工厂方法


三、结构型模式

⭐ 代理模式三种实现

一句话: 静态代理手写、JDK 动态代理基于接口(反射)、CGLIB 基于继承(字节码子类);Spring Boot 2.x+ 默认 CGLIB。

维度静态代理JDK 动态代理CGLIB
实现手写代理类Proxy + InvocationHandler字节码生成子类
要求相同接口必须有接口不能是 final
性能编译期确定,最快反射调用,较慢字节码直调,略快于 JDK
Spring有接口时默认Boot 2.x+ 默认

Spring AOP 选择逻辑:

有接口 → 默认 JDK 动态代理
无接口 → CGLIB
Boot 2.x+ → 全局默认 CGLIB(proxy-target-class=true)

⭐ 装饰器模式(Java IO)

一句话: 装饰器在不改变接口的前提下动态增强功能;Java IO 是最经典案例——FileInputStream 被 Buffered / Data 层层包装。

InputStream(抽象组件)
├── FileInputStream(具体组件)
└── FilterInputStream(装饰器基类)
    ├── BufferedInputStream  → 增加缓冲
    ├── DataInputStream      → 增加读基本类型
    └── PushbackInputStream  → 增加回退
java
// 装饰器层层包装
new DataInputStream(new BufferedInputStream(new FileInputStream("data.bin")))
// File: 读字节 → Buffered: +缓冲 → Data: +readInt/readDouble

装饰器 vs 代理: 装饰器增强功能不改语义;代理控制访问(权限 / 远程调用)

装饰器 vs 继承: 装饰器运行时动态组合,继承编译期静态确定 → 装饰器避免类爆炸


四、行为型模式

⭐ 策略模式替代 if/else

一句话: 定义策略接口 → 每种算法一个实现类 → Map/枚举做路由分发,彻底消除 if/else,符合开闭原则。

三板斧:

① 定义接口 NotifyStrategy
② 每种渠道一个实现类 @Component("sms") / @Component("email")
③ Map 分发:@Autowired Map<String, NotifyStrategy> → get(type).send()

Java 中的策略实例:

  • Comparator → 不同比较策略传给 Collections.sort()
  • ThreadPoolExecutor 拒绝策略 → Abort / CallerRuns / Discard
  • Spring Resource → ClassPath / FileSystem / Url

⭐ 观察者模式 + Spring Event

一句话: 一对多依赖,Subject 状态变化自动通知所有 Observer;Spring Event = ApplicationEventPublisher 发布 + @EventListener 订阅。

Spring Event 三步:

① 定义事件:class OrderCreatedEvent extends ApplicationEvent
② 发布事件:publisher.publishEvent(new OrderCreatedEvent(...))
③ 监听事件:@EventListener public void onOrderCreated(OrderCreatedEvent e)
维度说明
默认同步监听器在发布者线程执行
异步@Async + @EnableAsync
底层ApplicationEventMulticaster 维护 Listener 列表,遍历调用

⚠️ 观察者过多时同步通知会阻塞;可能引发循环依赖


⭐ 通知系统场景题(四种模式协作)

一句话: 观察者解耦事件触发 → 工厂根据类型获取策略 → 策略封装各渠道发送逻辑 → 模板方法统一流程(校验→组装→发送→日志)。

事件触发 ──观察者──▶ Listener ──工厂──▶ StrategyFactory

                              ┌────────────┼────────────┐
                              ▼            ▼            ▼
                           Sms策略     Email策略     Push策略  ← 策略模式
                              │            │            │
                              └────────────┼────────────┘

                              AbstractNotifyTemplate  ← 模板方法
                              (校验→组装→发送→日志)

💡 扩展性:新增渠道只需实现接口 + 注册 Bean,符合开闭原则


补充速览

题号关键词核心答案
Q2三大分类创建型(对象创建) / 结构型(类组合) / 行为型(通信职责);高频 8 个:单工建 + 代装适 + 策观模
Q6Builder复杂对象多可选参数用 Builder 链式构建;vs 工厂:Builder 关注构建过程,Factory 关注创建结果
Q7原型/深拷贝clone() 默认浅拷贝(引用共享);深拷贝:手动递归 clone 或序列化/反序列化
Q10适配器接口不兼容时做转换;HandlerAdapter 统一调用不同类型 Controller;InputStreamReader 字节→字符
Q11责任链请求沿链传递,每个节点决定处理或转发;Servlet Filter / Security Filter / Netty Pipeline
Q13模板方法/AQS父类定骨架(final)子类实现钩子;AQS: acquire(模板) 调 tryAcquire(子类);ReentrantLock/Semaphore
Q15状态 vs 策略状态:内部自动切换(客户透明);策略:外部指定(客户端决定)
Q17重构 if/else识别变化点 → 策略接口 → 每分支一个类 → Map 路由 → 公共流程加模板方法
Q18项目实战准备 3-4 个:单例(Bean) / 策略(多解析器) / 模板方法(导入流程) / 观察者(事件解耦)

🧠 助记汇总

口诀含义
单工建 代装适 策观模高频 8 模式:单例/工厂/建造者 + 代理/装饰器/适配器 + 策略/观察者/模板方法
封组接设计模式三大思想:封装变化 / 多用组合少用继承 / 面向接口编程
饿懒静枚单例四写法:饿汉式 / 懒汉DCL / 静态内部类 / 枚举
分初指DCL 重排问题:分配内存 / 初始化 / 指向引用——可能 1→3→2
接策图策略模式三板斧:接口 / 策略实现类 / Map 路由分发
定发监Spring Event 三步:定义事件 / 发布事件 / 监听事件
观工策模通知系统四模式协作:观察者/工厂/策略/模板方法

✅ 自测清单

#问题你能说出...
1SOLID 原则6 个原则名称 + 各举一个 Spring 示例
2Spring 设计模式至少 4 个模式 + 对应 Spring 组件
3单例写法4 种写法对比 + DCL 为什么要 volatile
4三种工厂简单/方法/抽象的区别 + 新增产品谁违反 OCP
5代理模式静态/JDK/CGLIB 对比 + Spring 如何选择
6装饰器Java IO 的装饰器结构 + 与代理/继承的区别
7策略模式替代 if/else 的三板斧 + Java 中的实例
8观察者Spring Event 三步 + 同步/异步区别
9通知系统场景四种模式如何协作 + 扩展性如何保证

💡 首次全部过一遍 → 第2天只过答不上来的 → 第4天再复习 → 面试前一天最后扫一遍