设计模式图解

从"背UML"到"看懂画面 + 代码对应" — 8大高频设计模式 × SVG结构图 × IDE实战

1单例模式 Singleton

一个国家只有一个总统——不管多少次选举,总统职位只有一个。无论多少个线程调用 getInstance(),拿到的都是同一个对象。
多线程获取单例 Thread-1 Thread-2 Thread-3 getInstance() Singleton - instance: Singleton - Singleton() + getInstance(): Singleton 唯一实例 instance 饿汉式 vs 懒汉式 饿汉式 (Eager) static final Singleton INSTANCE = new Singleton(); 类加载时创建 → 线程安全(类加载机制保证) → 可能浪费 懒汉式 (Lazy) + DCL if(instance==null) → synchronized → if(instance==null) 首次使用时创建 → 需DCL+volatile → 节省资源
双重检查锁 (DCL) 流程 getInstance() instance == null? No → return instance synchronized(Singleton.class) instance == null? No → return Yes → new + volatile
Singleton.js
src
EagerSingleton
LazyDCLSingleton
EnumSingleton
// 1. 饿汉式 — 模块级常量,JS 单线程无需同步 class EagerSingleton { constructor() { if (EagerSingleton.INSTANCE) { throw new Error('Use EagerSingleton.getInstance()'); } } static INSTANCE = new EagerSingleton(); static getInstance() { return EagerSingleton.INSTANCE; } } // 2. 懒汉式 — JS 单线程,无需 DCL/volatile,闭包封装 const LazyDCLSingleton = (function() { let instance; class LazyDCLSingleton { constructor() { if (instance) { throw new Error('Use LazyDCLSingleton.getInstance()'); } } } return { getInstance() { if (!instance) { // 首次检查 instance = new LazyDCLSingleton(); } return instance; } }; })(); // 3. 冻结对象单例 — JS 无枚举,用 Object.freeze 模拟 const EnumSingleton = Object.freeze({ doSomething() { console.log('枚举单例'); } });
真实项目:Spring Bean 默认 scope="singleton";数据库连接池(HikariDataSource);Logger(slf4j)都是单例。Spring 的单例是基于 IoC 容器管理的,不是传统的类加载机制。
单例模式面试坑点:① 饿汉式类加载就创建,可能浪费资源;② 懒汉式不加 synchronized 线程不安全;③ DCL 必须加 volatile(防止指令重排序:new 操作不是原子的,分配内存→初始化→引用赋值,重排后可能返回未初始化的对象);④ 反射可破坏单例(枚举单例天然防反射);⑤ 序列化/反序列化会创建新实例(需实现 readResolve())。
三种单例实现对比 饿汉式 static final 直接创建 ✅ 天然线程安全 ✅ 实现最简单 ❌ 类加载即创建 适合:实例轻量且必用 如:Runtime.getRuntime() 懒汉式+DCL 双重检查锁 + volatile ✅ 延迟加载 ✅ 首次使用才创建 ❌ 实现较复杂 适合:实例创建成本高 如:数据库连接池初始化 枚举单例 ⭐推荐 enum 天然单例 ✅ 线程安全 ✅ 防反射/序列化攻击 ✅ Effective Java推荐 适合:大部分场景 如:全局配置、常量池

2工厂模式 Factory

买车——你只说"我要一辆SUV",4S店(工厂)负责生产具体型号。你不需要知道车怎么组装的,只需要告诉工厂类型。
简单工厂 Client SimpleFactory create(type) create("A") ProductA ProductB 工厂方法 Client «abstract» Factory +create(): Product FactoryA FactoryB ProductA ProductB 抽象工厂 «abstract» AbstractFactory createA() + createB() Factory1 → A1 + B1 Factory2 → A2 + B2 A1, B1 A2, B2
FactoryPattern.js
factory
SimpleFactory
FactoryMethod
SpringFactoryBean
// 简单工厂 — 违反开闭原则,但简单实用 class CarFactory { static create(type) { switch (type) { case 'suv': return new SUVCar(); case 'sedan': return new SedanCar(); default: throw new Error('Unknown type'); } } } // 工厂方法 — 每个产品对应一个工厂子类(JS 无接口,注释约定) // interface CarFactory { create() } class SUVCarFactory { // implements CarFactory create() { return new SUVCar(); } } // Spring FactoryBean — Spring专用的工厂模式(仅作参考,JS 无对应概念) // Spring @Component class MyFactoryBean { // implements FactoryBean getObject() { return new MyService(); } getObjectType() { return MyService; } }
真实项目:Spring BeanFactory / ApplicationContext 是最大的工厂;LoggerFactory.getLogger();MyBatis 的 SqlSessionFactory。
工厂模式选择指南:产品只有2-3种且不太会新增 → 简单工厂(if-else够了);产品经常新增 → 工厂方法(符合开闭原则);需要创建一整套产品族 → 抽象工厂(如跨数据库切换 DAO 全家桶)。面试时一定要结合具体场景说,不要只背概念。

工厂模式三级对比

维度简单工厂工厂方法抽象工厂
工厂个数1个多个(每产品一个)多个(每产品族一个)
产品维度单个产品单个产品产品族(多个产品)
开闭原则❌ 违反✅ 符合✅ 符合
新增产品改工厂类代码加一个工厂类加一个工厂类+产品族
复杂度
Spring应用FactoryBean切换数据源DAO

3观察者/发布订阅模式 Observer / Pub-Sub

关注UP主——UP发新视频,所有粉丝收到通知。观察者模式是直接通知粉丝,发布订阅则是通过B站(调度中心)间接通知。
观察者模式 (Observer) Subject +attach() +notify() -observers: List ObserverA ObserverB ObserverC notify() 发布订阅模式 (Pub-Sub) Publisher EventBus / Topic 调度中心(解耦) SubscriberA SubscriberB publish() dispatch() 观察者:Subject 直接 notify 发布者和订阅者有直接依赖 发布订阅:经调度中心中转 发布者和订阅者完全解耦
ObserverPattern.js
observer
SpringEvent
VueEventBus
// Node.js EventEmitter 机制(发布订阅,对标 Spring Event) // 1. 定义事件(JS 中事件就是普通对象) class OrderCreatedEvent { constructor(source, order) { this.source = source; this.order = order; } } // 2. 发布事件 — 用 EventEmitter 替代 Spring ApplicationEventPublisher // Spring @Service class OrderService { constructor(emitter) { this.emitter = emitter; // 替代 @Autowired ApplicationEventPublisher } createOrder(order) { // 保存订单... this.emitter.emit('orderCreated', new OrderCreatedEvent(this, order)); } } // 3. 监听事件 — 可多个订阅者 // Spring @Component class EmailNotifier { constructor(emitter) { emitter.on('orderCreated', (event) => { // 替代 @EventListener // 发送邮件通知 }); } } // Vue3 EventBus(mitt库) // emitter.ts import mitt from 'mitt' export const emitter = mitt() // 发布 emitter.emit('orderCreated', { id: '123' }) // 订阅 emitter.on('orderCreated', (data) => { console.log('收到通知', data) })
JDK 的 Observable/Observer 在 JDK 9 中已废弃,推荐用 Spring Event 或 java.beans.PropertyChangeListener 替代。

观察者 vs 发布订阅 何时选择?

场景推荐模式原因
同一进程内,组件间通信观察者简单直接,无中间层
跨模块/跨服务解耦发布订阅调度中心解耦,可跨进程
Spring应用内事件发布订阅ApplicationEventPublisher 充当调度中心
Vue/React 组件通信发布订阅EventBus / Redux store 中转
微服务间消息发布订阅MQ(Kafka/RabbitMQ)就是调度中心

4策略模式 Strategy

出行策略——赶时间选飞机,省钱选火车,锻炼选步行,策略随时切换。核心是"算法族,分别封装,互相替换"。
策略模式结构 Context -strategy: Strategy +execute() «interface» Strategy +algorithm() 持有引用 FlyStrategy TrainStrategy WalkStrategy if-else 地狱 vs 策略模式 ❌ if-else 地狱 if(type=="fly") { ... } else if(type=="train") { ... } else if(type=="walk") { ... } ✅ 策略模式 strategyMap.get(type).algorithm(); 新增策略只需加一个实现类 完全符合开闭原则
StrategyPattern.js
strategy
PayStrategy
SpringInject
// 支付策略接口(JS 无接口,用注释约定 + 鸭子类型) // interface PayStrategy { getType(); pay(amount); } // Spring @Component class WechatPayStrategy { // implements PayStrategy getType() { return 'wechat'; } pay(amount) { /* 微信支付 */ } } // Spring @Component class AlipayStrategy { // implements PayStrategy getType() { return 'alipay'; } pay(amount) { /* 支付宝支付 */ } } // 手动注册策略到 Map(模拟 Spring @Autowired Map 注入) // Spring @Service class PayService { constructor() { this.strategyMap = new Map([ ['wechat', new WechatPayStrategy()], ['alipay', new AlipayStrategy()] ]); } pay(type, amount) { const strategy = this.strategyMap.get(type); strategy.pay(amount); } }
真实项目:Spring 的 Resource 接口(ClassPathResource/FileSystemResource/UrlResource)就是策略模式;Comparator 是排序策略。

策略模式消除 if-else 的完整步骤

// Step 0: if-else 地狱(改造前) pay(type, amount) { if (type === 'wechat') { wechatPayService.pay(amount); } else if (type === 'alipay') { alipayPayService.pay(amount); } else if (type === 'card') { cardPayService.pay(amount); } // 新增支付方式?继续加 else if 😱 } // Step 1: 定义策略接口(JS 用注释约定) // interface PayStrategy { getType(); pay(amount); } // Step 2: 每种方式实现策略 // Spring @Component('wechat') class WechatPayStrategy { ... } // implements PayStrategy // Spring @Component('alipay') class AlipayStrategy { ... } // implements PayStrategy // Step 3: 手动注册到 Map(模拟 Spring 自动注入) // Spring @Service class PayService { constructor() { this.payStrategyMap = new Map([ ['wechat', new WechatPayStrategy()], ['alipay', new AlipayStrategy()] ]); } pay(type, amount) { this.payStrategyMap.get(type).pay(amount); } // 新增支付方式?加个类并注册即可,零修改 ✅ }

5装饰器模式 Decorator

穿衣服——T恤 → 外套 → 围巾,每层添加新功能,不改变原来的你。装饰器和被装饰者实现同一接口,层层包装。
装饰器模式结构 «interface» Component +operation() ConcreteComponent Decorator -component: Component ConcreteDecoratorA ConcreteDecoratorB 装饰链调用过程 DecoratorB DecoratorA ConcreteComponent wrap wrap new DecB(new DecA(new Comp())) 调用: B.op() → A.op() → Comp.op() → 逐层返回增强
DecoratorPattern.js
decorator
JavaIO
SpringInterceptor
TSDecorator
// Node.js Stream 装饰链 — JS 版的 I/O 装饰器 import { createReadStream } from 'fs' import { createGzip } from 'zlib' const stream = createReadStream('data.txt') // 被装饰者 .pipe(createGzip()); // 装饰器: 压缩 // 调用链: read() → gzip transform → 输出 // Stream 通过 pipe 串联,类似装饰器层层包装 // Express middleware 装饰链(类似 Spring HandlerInterceptor) // Spring @Component function authMiddleware(req, res, next) { // 前置增强:鉴权 return next(); // return true } // JS 装饰器语法(TC39 提案,需 babel 支持) function log(target, key, desc) { const orig = desc.value desc.value = function(...args) { console.log(`调用 ${key}`) return orig.call(this, ...args) } } class Svc { @log doWork() { } }
真实项目:Java I/O 全家桶(InputStream/OutputStream/Reader/Writer);Spring AOP 代理装饰;Vue mixin 混入增强。

Java I/O 装饰链全景

Java I/O 装饰链示例 FileInputStream BufferedInputStream DataInputStream wrap wrap Component: InputStream (抽象组件) ConcreteComponent: FileInputStream (被装饰者) Decorator: FilterInputStream (抽象装饰器) ConcreteDecorator: BufferedInputStream / DataInputStream new DataInputStream(new BufferedInputStream(new FileInputStream("file")))

6代理模式 Proxy

明星经纪人——你见不到明星本人,但经纪人可以帮你转达。经纪人还能加价(前置增强)和收尾(后置增强),而你毫不知情。
静态代理 Client Proxy RealSubject call delegate JDK 动态代理 Proxy.newProxy 运行时生成$Proxy类 InvocationHandler → RealSubject invoke CGLIB 代理 Enhancer.create() 生成目标类的子类 MethodInterceptor → 父类方法 三种代理方式对比 静态代理 手写代理类,编译期确定 一个接口一个代理类 ❌ 接口变则代理类变 ✅ 易理解 JDK 动态代理 运行时生成 $Proxy 类 必须实现接口 ❌ 目标类须有接口 ✅ Spring 默认选择 CGLIB 代理 运行时生成目标子类 不需要接口,覆盖方法 ❌ final 类/方法不可代理 ✅ 无接口也能代理
ProxyPattern.js
proxy
JDKDynamicProxy
CGLIBProxy
SpringAOP
// JS Proxy — 原生动态代理(类似 JDK 动态代理) const proxy = new Proxy(target, { get(obj, prop) { const orig = obj[prop]; if (typeof orig !== 'function') return orig; return (...args) => { // 前置增强 console.log('before: ' + prop); const result = orig.apply(obj, args); // 调用目标 // 后置增强 console.log('after: ' + prop); return result; }; } }); // JS Proxy 代理类实例(类似 CGLIB 代理,无需接口) const realSubject = new RealSubject(); const cglibProxy = new Proxy(realSubject, { get(obj, prop) { const orig = obj[prop]; if (typeof orig !== 'function') return orig; return (...args) => { console.log('cglib before'); const result = orig.apply(obj, args); console.log('cglib after'); return result; }; } }); // Spring AOP @AspectJ 注解方式(仅作参考,JS 无对应注解) // Spring @Aspect @Component class LogAspect { // Spring @Before('execution(* com.example.service.*.*(..))') before() { /* 前置通知 */ } // Spring @AfterReturning('execution(* com.example.service.*.*(..))') afterReturning() { /* 后置通知 */ } }
真实项目:Spring AOP(JDK动态代理/CGLIB);RPC远程代理(调用远程方法像调用本地方法);MyBatis MapperProxy(接口没有实现类,代理生成SQL执行)。

代理模式 vs 装饰器模式 深度对比

维度代理 Proxy装饰器 Decorator
核心目的控制对象访问动态增强功能
创建者代理方创建被代理对象客户端组合装饰链
客户端是否知情不知道(透明代理)知道(主动装饰)
包装层数通常1层可多层嵌套
典型场景远程代理、延迟加载、权限控制Java I/O、拦截器链
Spring体现AOP @Aspect → 动态代理Interceptor chain → 装饰器链
面试话术"控制访问,经纪人模式""增强功能,穿衣服模式"
面试高频坑:Spring AOP 既有代理思想也有装饰器思想。代理体现在:客户端不知道代理存在,代理控制对象访问。装饰器体现在:拦截器链层层增强,类似装饰器嵌套。建议先答代理模式,再补充"从拦截器链角度看也有装饰器思想"。

7适配器模式 Adapter

出国充电——中国插头插不进欧洲插座,加一个转换头(适配器)。适配器让两个不兼容的接口协同工作。
类适配器(extends + implements) «interface» Target +request() Adapter implements Adaptee extends Adapter extends Adaptee implements Target request() { super.specificRequest(); } 对象适配器(has-a + implements) «interface» Target +request() Adapter -adaptee: Adaptee Adaptee has-a Adapter has-a Adaptee implements Target request() { adaptee.specificRequest(); } 类适配器 extends Adaptee → 单继承限制 可直接调用父类方法,无需委派 对象适配器 ✅推荐 has-a Adaptee → 更灵活 符合组合优于继承原则
AdapterPattern.js
adapter
ClassAdapter
ObjectAdapter
HandlerAdapter
// 类适配器(JS 无接口,注释约定) class ClassAdapter extends Adaptee { // implements Target request() { super.specificRequest(); // 直接调用父类 } } // 对象适配器(推荐) class ObjectAdapter { // implements Target constructor(adaptee) { this.adaptee = adaptee; } request() { this.adaptee.specificRequest(); // 委派给 adaptee } } // Spring HandlerAdapter — 适配不同类型的Handler(仅作参考) // interface HandlerAdapter { supports(handler); handle(req, ...); } class HandlerAdapter { supports(handler) { /* ... */ } handle(req) { /* ... */ } } // RequestMappingHandlerAdapter 适配 @RequestMapping // HttpRequestHandlerAdapter 适配 HttpRequestHandler
真实项目:Arrays.asList() 把数组适配成 List;Spring MVC HandlerAdapter 适配多种 Handler;InputStreamReader 把 InputStream 适配成 Reader。

适配器模式在 Spring MVC 中的真实应用

Spring DispatcherServlet + HandlerAdapter DispatcherServlet 只认 HandlerAdapter 接口 RequestMappingHandlerAdapter HttpRequestHandlerAdapter SimpleControllerHandlerAdapter 适配 @RequestMapping 方法 HttpRequestHandler Controller 接口 为什么需要 HandlerAdapter? DispatcherServlet 不知道 Handler 的具体类型(可能是 @RequestMapping、Controller 接口、HttpRequestHandler) HandlerAdapter 把不同类型的 Handler 适配成统一的 handle() 调用 → 典型的适配器模式! 新增 Handler 类型?只需加一个 HandlerAdapter 实现类,无需修改 DispatcherServlet

8模板方法模式 Template Method

做菜模板——烧油 → 炒菜 → 调味,具体每步做什么菜不同(猪肉白菜 vs 牛肉土豆),但步骤模板固定。父类定骨架,子类填细节。
模板方法模式结构 AbstractClass +templateMethod() { step1(); step2(); step3(); } #step1() #step2() #step3() #hook() { /* 可选钩子 */ } ConcreteClassA step1() → 烧油(大豆油) step2() → 炒猪肉白菜 step3() → 加酱油调味 ConcreteClassB step1() → 烧油(橄榄油) step2() → 炒牛肉土豆 step3() → 加盐调味 调用流程 templateMethod() → step1() → step2() → step3() → hook()? → 完成
TemplateMethodPattern.js
template
AbstractCook
SpringRefresh
JdbcTemplate
// 模板方法模式(JS 无 abstract,用抛错模拟抽象方法) class AbstractCook { // 模板方法 — 定义算法骨架(JS 无法 final,约定不覆写) cook() { this.heatOil(); this.stirFry(); this.season(); if (this.wantGarnish()) { // 钩子方法 this.garnish(); } } heatOil() { throw new Error('子类必须实现 heatOil'); } stirFry() { throw new Error('子类必须实现 stirFry'); } season() { throw new Error('子类必须实现 season'); } wantGarnish() { return false; } // 钩子默认实现 garnish() {} // 可选覆写 } // Spring AbstractApplicationContext.refresh() — 经典模板方法(仅作参考) // refresh() 定义了12个步骤,子类可覆写某些步骤: // prepareRefresh() → obtainBeanFactory() → ... // → finishRefresh() // JS 回调式模板(类似 Spring JdbcTemplate 的回调模式) function withConnection(dataSource, callback) { const conn = dataSource.getConnection(); try { return callback(conn); // 用户只需提供 SQL + 回调 } finally { conn.close(); // 模板负责关闭连接 } } const users = withConnection(dataSource, (conn) => { return conn.query('SELECT * FROM user').map(row => row.name); }); // withConnection 封装了: 获取连接 → 执行回调 → 关闭连接 // 用户只需要提供 SQL 处理逻辑(回调)
真实项目:Spring AbstractApplicationContext.refresh() 是最经典的模板方法,定义了容器刷新的12个步骤;JdbcTemplate 封装了 JDBC 样板代码;HttpServlet 的 doGet()/doPost() 也是模板方法的变体。

Spring refresh() 模板方法拆解

// AbstractApplicationContext.refresh() — 模板方法(简化版,JS 风格) refresh() { this.prepareRefresh(); // 步骤1: 准备工作 const bf = this.obtainFreshBeanFactory(); // 步骤2: 创建BeanFactory this.prepareBeanFactory(bf); // 步骤3: 配置BeanFactory this.postProcessBeanFactory(bf); // 步骤4: 子类可覆写 👈 钩子 this.invokeBeanFactoryPostProcessors(bf); // 步骤5: 后置处理器 this.registerBeanPostProcessors(bf); // 步骤6: 注册BeanPostProcessor this.initMessageSource(); // 步骤7: 国际化 this.initApplicationEventMulticaster(); // 步骤8: 事件广播器 this.onRefresh(); // 步骤9: 子类可覆写 👈 钩子 this.registerListeners(); // 步骤10: 注册监听器 this.finishBeanFactoryInitialization(bf);// 步骤11: 初始化单例Bean this.finishRefresh(); // 步骤12: 完成刷新 } // 子类如 AnnotationConfigServletWebServerApplicationContext // 可覆写 onRefresh() 来创建内嵌 Tomcat 👈 典型的钩子方法

汇总

设计模式分类总表

类型模式一句话Spring中的应用面试频率
创建型单例 Singleton全局唯一实例,节省资源Bean 默认 singleton scope★★★★★
创建型工厂 Factory将创建逻辑封装,客户端不关心细节BeanFactory / FactoryBean★★★★★
行为型观察者 Observer一对多依赖,状态变化自动通知ApplicationEvent + @EventListener★★★★
行为型策略 Strategy算法族分别封装,运行时可替换Resource 接口 / @Autowired Map★★★★
结构型装饰器 Decorator动态添加功能,不改变原对象HandlerInterceptor / AOP包装★★★★
结构型代理 Proxy控制访问,增强而不改变接口AOP (JDK/CGLIB代理)★★★★★
结构型适配器 Adapter接口转换,让不兼容的类协作HandlerAdapter★★★
行为型模板方法 Template父类定骨架,子类填细节AbstractApplicationContext.refresh()★★★★

三种结构型模式易混淆对比

维度装饰器 Decorator代理 Proxy适配器 Adapter
目的增强功能(加料)控制访问(经纪人)接口转换(转换头)
对象创建运行时层层组合代理方控制创建已有类需要适配
接口关系与被装饰者同接口与目标对象同接口转换到目标接口
客户端感知知道装饰链的存在不知道代理的存在通过新接口使用
层数可多层嵌套通常一层通常一层
Spring应用HandlerInterceptorAOP / MapperProxyHandlerAdapter
类比穿衣服(层层加)经纪人(替你办事)充电转换头

面试速答

Q: "Spring AOP用了什么设计模式?"
代理模式 + 装饰器模式。Spring AOP 的核心是动态代理(JDK 动态代理或 CGLIB 代理),在运行时为目标对象生成代理类,在代理类中织入增强逻辑(前置/后置/环绕通知)。从增强功能角度看,也有装饰器的思想——但不改变原对象接口。面试时先答代理模式,再补充装饰器角度,体现深度。
Q: "工厂模式和简单工厂区别?"
简单工厂是一个工厂类用 if-else/switch 决定创建哪种产品,违反开闭原则,新增产品要改工厂代码。工厂方法是每个产品对应一个工厂子类,新增产品只需新增工厂类,符合开闭原则。简单工厂够用时不要过度设计——3个以下产品用简单工厂就行。抽象工厂更进一步,创建产品族(一组相关产品),如跨数据库时切换整套 DAO。
Q: "观察者和发布订阅什么区别?"
核心区别在于是否有调度中心。观察者模式中 Subject 直接维护观察者列表并 notify,发布者和订阅者有直接依赖;发布订阅模式引入 EventBus/Topic 调度中心,发布者和订阅者完全解耦。Spring Event 是发布订阅模式(经 ApplicationEventPublisher 中转),Vue 的响应式系统本质是观察者模式(Dep 直接 notify Watcher)。
Q: "策略模式怎么消除 if-else?"
三步走:① 定义策略接口(如 PayStrategy),② 每种情况实现一个策略类(WechatPayStrategy、AlipayStrategy),③ 用 Map 自动注入——Spring 中 @Autowired Map<String, PayStrategy> 会自动将所有实现注入,key 是 bean 名。调用时 strategyMap.get(type).pay() 一行搞定,新增支付方式只需加一个 @Component 类,零修改原有代码。
Q: "装饰器和代理模式有什么区别?"
目的不同:装饰器是"增强功能",像穿衣服层层加功能;代理是"控制访问",像经纪人不让你直接见明星。客户端感知不同:装饰器客户端通常主动组合装饰链;代理客户端不知道代理存在。层数不同:装饰器可多层嵌套(BufferedInputStream 套 FileInputStream);代理通常就一层。Spring AOP 表面是代理,但拦截器链是装饰器思想的体现。
Q: "你项目里用过哪些设计模式?"
按频率回答:① 策略模式——支付方式/折扣策略,用 @Autowired Map 自动注入消除 if-else;② 模板方法——导出报表(PDF/Excel/CSV)步骤固定,具体格式不同;③ 工厂模式——不同渠道的消息发送器(短信/邮件/推送);④ 观察者/发布订阅——订单创建后触发库存扣减、积分发放、消息通知。关键:每个模式都要结合具体业务场景说,不要干巴巴念定义。

实战建议

面试说设计模式要结合 Spring 具体例子。不要说"我知道单例模式",要说"Spring Bean 默认是单例的,但和传统的饿汉式不同,Spring 的单例是由 IoC 容器管理的,每个 ApplicationContext 中只有一个实例"。有场景有细节,才显得你真正用过。
不要过度设计——3个以上 if-else 才考虑策略模式,2个的时候简单 if-else 更清晰。设计模式是为了解决问题,不是为了炫技。先写直白的代码,重构时再引入模式,这才是正常的工作流。
代理模式是理解 Spring AOP 和 MyBatis 的关键。Spring AOP 用 JDK 动态代理(有接口)或 CGLIB(无接口),MyBatis 的 MapperProxy 让你只写接口就能执行 SQL。理解了代理,Spring 框架的很多"魔法"就不再神秘了。