设计模式与编码
面试权重:★★ | 适用级别:P7/P8+ | 预计复习时间:1周
概览
面试权重:★★ | 适用级别:P7/P8+ | 预计复习时间:1周
这一章真正考的不是“能背出 23 种模式”,而是你能否说明什么时候该抽象、为什么这么设计、以及模式如何在业务和框架里落地。
建议用法
正文用于建立“问题 -> 设计原则 -> 模式选型 -> 框架映射”的理解链路;高频题用于压缩成 30 秒口述。复习时优先把相近模式成对记忆,比如策略 vs 模板方法、装饰器 vs 代理、工厂方法 vs 抽象工厂。
一、知识体系
1. 设计原则(SOLID) ★★★
1.1 SOLID原则
S — 单一职责原则(SRP)
- 核心结论
- 单一职责不是“一个类只写一个方法”,而是“一个模块只承受一类变化原因”。
- 原理展开
- 如果一个类同时负责参数校验、业务编排、持久化、通知下游,那么只要任一环节变化,这个类都要改,耦合度会快速上升。
- SRP 的收益是降低变更波及面,提升可测试性和可替换性。
- 面试怎么答
- “我理解的单一职责是按变化原因拆分,而不是机械按功能点切类。比如支付服务里的风控、路由、渠道调用、回调通知就不应该全堆在一个 Service 里。”
- 易错点
- 过度拆分类也会让系统碎片化,SRP 不是无限拆分。
O — 开闭原则(OCP)
- 核心结论
- 对扩展开放、对修改关闭,本质是通过抽象把变化点提前隔离出来。
- 原理展开
- 最常见落地手法是接口、多态、策略注册表、插件机制,而不是硬背概念。
- 面试怎么答
- “开闭原则不是说永远不改旧代码,而是新增变化时优先通过扩展接入,避免频繁修改稳定主流程。”
- 易错点
- 不要为了追求 OCP 提前做大量并不存在的抽象,这会违反 YAGNI。
L — 里氏替换原则(LSP)
- 核心结论
- 子类能替换父类而不破坏调用方预期,重点是行为契约一致。
- 原理展开
- 如果父类承诺“调用一定成功”,子类却偷偷抛更多异常;或者父类支持全部输入,子类却收紧条件,都属于违背 LSP。
- 面试怎么答
- “LSP 不是语法上的继承关系,而是语义上的可替换性。子类不能破坏父类的前置条件、后置条件和不变量。”
- 易错点
- 继承不等于可复用。很多不合适的继承,本质上就是 LSP 已经被破坏了。
I — 接口隔离原则(ISP)
- 核心结论
- 客户端不应该依赖它不需要的方法,接口应该小而专。
- 原理展开
- 大接口通常导致“实现类被迫实现无关方法”“调用方依赖了不需要的能力”,最终演变成僵硬设计。
- 面试怎么答
- “接口隔离的目标是减少无效依赖。尤其在跨服务边界和 SDK 设计里,接口越聚焦,后续演进越稳。”
D — 依赖倒置原则(DIP)
- 核心结论
- 高层模块和低层模块都应该依赖抽象,而不是高层直接依赖具体实现。
- 原理展开
- DIP 是工厂、策略、DDD 分层、IOC 容器得以成立的重要基础。
- 面试怎么答
- “依赖倒置让业务编排依赖接口,具体实现由容器或工厂注入。这样高层流程稳定,底层实现才有替换空间。”
- 易错点
- 依赖抽象不等于项目里所有类都必须抽接口。稳定且不会替换的简单实现,可以保留直接依赖。
1.2 其他重要原则 ★★
- DRY
- 核心结论:避免重复的不是代码行数,而是知识和规则的重复来源。
- KISS
- 核心结论:优先选最直接、团队能长期维护的方案。
- YAGNI
- 核心结论:不要为假想需求提前建复杂扩展点。
- 组合优于继承
- 核心结论:组合更灵活、更容易控制边界,也更不容易破坏父类契约。
- 迪米特法则
- 核心结论:模块之间只暴露必要交互,减少“深链式调用”。
2. 创建型模式 ★★
2.1 单例模式(Singleton) ★★★
- 核心结论
- 单例的价值在于控制全局唯一实例和统一访问入口,但它天然带来生命周期、测试隔离和隐藏依赖问题。
常见实现方式
- 饿汉式:类加载即创建,简单但可能提前占资源。
- 懒汉式:延迟初始化,但线程安全实现容易出错。
- DCL(双重检查锁):
volatile + synchronized,兼顾延迟加载和并发安全。 - 静态内部类:利用类加载的线程安全特性实现延迟初始化。
- 枚举单例:最稳妥,天然防反射与反序列化破坏。
DCL(双重检查锁):volatile + synchronized
- 原理展开
new不是原子操作,可能发生“分配内存 -> 引用赋值 -> 对象初始化”的指令重排。- 没有
volatile时,其他线程可能读到一个“引用非空但对象未初始化完成”的实例。
- 面试怎么答
- “DCL 的难点不在双重
if,而在volatile要禁止重排序,否则会拿到半初始化对象。”
- “DCL 的难点不在双重
- 易错点
- 不要只说
volatile保证可见性,面试里最好补上“禁止重排序”。
- 不要只说
Spring Bean默认单例的实现(三级缓存)
- 核心结论
- Spring 的单例不是 GoF 单例模式的简单翻版,它更像是 IOC 容器托管下的单例作用域。
- 面试怎么答
- “Spring 默认单例的重点在容器生命周期和依赖注入,不只是‘全局只 new 一次’。三级缓存主要是为了解循环依赖场景中的提前暴露。”
2.2 工厂模式(Factory) ★★★
- 核心结论
- 工厂模式解决的是“对象创建逻辑和使用逻辑解耦”,不只是把
new换个地方写。
- 工厂模式解决的是“对象创建逻辑和使用逻辑解耦”,不只是把
简单工厂 / 工厂方法 / 抽象工厂
- 原理展开
- 简单工厂:一个工厂里根据参数分支创建所有产品,适合产品少、变化少的场景。
- 工厂方法:每个产品或产品类型有自己的创建入口,符合开闭原则。
- 抽象工厂:一次创建一组有关联的产品族,适合主题化、平台化、跨环境适配。
- 面试怎么答
- “简单工厂适合控制入口,工厂方法适合扩展单个产品,抽象工厂适合扩展整组产品族。三者复杂度和扩展维度不同。”
- 易错点
- 不要把“创建对象”都叫工厂。关键是有没有把创建规则抽离成稳定边界。
Spring中的应用:BeanFactory、FactoryBean
- 核心结论
BeanFactory是容器级工厂,FactoryBean是让某个 Bean 的创建逻辑也工厂化。
- 面试怎么答
- “前者是 Spring 的整体创建机制,后者是把单个复杂对象的创建过程托管给一个特殊 Bean。”
2.3 建造者模式(Builder) ★★
- 核心结论
- Builder 适合“参数多、构建过程分步、对象需要保持创建后不可变或一致性”的场景。
- 原理展开
- Builder 把复杂对象的装配过程显式化,避免 telescoping constructor。
- Lombok
@Builder是语法层简化,但设计意图仍然是“分步构建复杂对象”。
- 面试怎么答
- “如果对象参数多且可选项多,我会优先考虑 Builder;如果还存在固定的构建顺序,再考虑引入 Director。”
- 易错点
- 不要把链式 setter 当成完整 Builder。真正的 Builder 通常会在
build()时做一致性校验。
- 不要把链式 setter 当成完整 Builder。真正的 Builder 通常会在
2.4 原型模式(Prototype) ★
- 核心结论
- 原型模式适合复制成本高或想快速生成相似对象的场景,但 Java 里
Cloneable体验并不理想。
- 原型模式适合复制成本高或想快速生成相似对象的场景,但 Java 里
- 原理展开
- 浅拷贝只复制外壳,引用字段仍共享。
- 深拷贝要递归复制对象图,业务里更常见的做法是显式拷贝构造或映射转换。
- 面试怎么答
- “原型模式的核心是复制而不是创建,但 Java 里我通常不会直接推荐
clone(),而是更倾向显式拷贝。”
- “原型模式的核心是复制而不是创建,但 Java 里我通常不会直接推荐
3. 结构型模式 ★★
3.1 代理模式(Proxy) ★★★
- 核心结论
- 代理模式的核心意图是控制访问、增强横切能力,而不是简单包装一下对象。
静态代理
- 原理展开
- 代理类和目标类实现同一接口,适合逻辑简单、类型数量少的场景。
JDK动态代理:基于接口(InvocationHandler)
- 核心结论
- 依赖接口,生成代理对象时把方法调用统一转发给
InvocationHandler。
- 依赖接口,生成代理对象时把方法调用统一转发给
CGLIB动态代理:基于继承(MethodInterceptor)
- 核心结论
- 通过生成子类织入增强逻辑,适用于没有接口的类,但不能代理
final类和final方法。
- 通过生成子类织入增强逻辑,适用于没有接口的类,但不能代理
JDK vs CGLIB 区别
- 面试怎么答
- “JDK 动态代理基于接口,侵入性低;CGLIB 基于继承,适用面更广但受 final 限制。Spring AOP 会根据目标类型选择,现代版本也常统一偏向 CGLIB 以减少行为差异。”
- 易错点
- 不要把代理模式和装饰器模式混为一谈。二者结构相似,但意图不同。
Spring AOP的实现:JDK代理(接口) + CGLIB(类)
- 核心结论
- AOP 是代理模式在框架里的经典落地,重点不是“代理本身”,而是把事务、日志、权限这类横切关注点抽出来。
3.2 装饰器模式(Decorator) ★★
- 核心结论
- 装饰器解决的是“在不改原类的前提下叠加能力”,适合能力可组合扩展的场景。
- 原理展开
- Java IO 流是典型案例,
BufferedInputStream、DataInputStream都是在原始流上叠加能力。
- Java IO 流是典型案例,
- 面试怎么答
- “装饰器强调能力叠加,代理强调访问控制或增强。结构可能像,但设计意图不同。”
3.3 适配器模式(Adapter) ★★
- 核心结论
- 适配器不是为了“设计得优雅”,而是为了隔离不兼容接口,减少调用方被外部模型污染。
- 原理展开
- 类适配器基于继承,受单继承限制。
- 对象适配器基于组合,更灵活,也更常见。
- 面试怎么答
- “我在业务里几乎都用对象适配器,因为它能把外部 DTO 和内部领域模型清晰隔离开。”
- 易错点
- 适配器不是 DTO 转换工具的另一个名字。关键是要隔离接口差异和依赖边界。
3.4 其他结构型 ★
- 外观模式(Facade)
- 核心结论:对复杂子系统提供统一入口,适合对外暴露简单 API。
- 桥接模式(Bridge)
- 核心结论:把抽象和实现两个变化维度拆开,避免类爆炸。
- 组合模式(Composite)
- 核心结论:用树形结构统一处理单个对象和组合对象。
- 享元模式(Flyweight)
- 核心结论:通过共享内部状态减少对象数量,典型如 Integer 缓存池、字符串常量池。
4. 行为型模式 ★★
4.1 策略模式(Strategy) ★★★
- 核心结论
- 策略模式解决的是“同一目标下存在多种可替换算法或处理路径”,它对消除大段
if-else/switch很有效。
- 策略模式解决的是“同一目标下存在多种可替换算法或处理路径”,它对消除大段
if-else/switch消除利器
- 原理展开
- 先定义统一策略接口,再把每种规则封装为独立实现,最后通过注册表、工厂或 IOC 容器按 key 路由。
- 面试怎么答
- “策略模式不是为了炫技,而是把分支逻辑从主流程里抽出来,让新增策略不必反复改旧逻辑。”
- 易错点
- 分支很少、变化频率很低的场景,直接
if-else可能更简单,不必强行上策略。
- 分支很少、变化频率很低的场景,直接
Spring中的应用:多实现注入Map/List
- 核心结论
- Spring 容器天然适合做策略注册中心,通过
Map<String, Strategy>就能把实现收集起来。
- Spring 容器天然适合做策略注册中心,通过
4.2 模板方法模式(Template Method) ★★
- 核心结论
- 模板方法适合“流程稳定、步骤可变”的场景,通过继承约束算法骨架。
- 原理展开
- 父类定义固定主流程,子类实现差异步骤或钩子方法。
JdbcTemplate、HttpServlet、AQS 都体现了这一思路。
- 面试怎么答
- “模板方法强调流程骨架稳定,策略模式强调算法整体可替换。一个偏继承,一个偏组合。”
- 易错点
- 当变化点很多、子类分支越来越多时,模板方法会变僵硬,此时往往要转向组合或策略。
4.3 观察者模式(Observer) ★★
- 核心结论
- 观察者模式解决的是一对多通知,重点是解耦事件发送方和处理方。
- 原理展开
- Spring 事件机制、MQ 的发布订阅、领域事件扩展都可以从观察者角度理解。
- 面试怎么答
- “观察者适合异步扩展和跨模块通知,但要注意事件幂等、失败补偿和链路可观测性。”
4.4 责任链模式(Chain of Responsibility) ★★
- 核心结论
- 责任链适合把一串处理步骤按顺序组织起来,让每个节点只关心自己的处理逻辑和是否向后传递。
- 原理展开
- Servlet Filter、Spring Interceptor、Netty Pipeline、Sentinel SlotChain 都是典型案例。
- 面试怎么答
- “责任链特别适合校验、鉴权、限流、风控这类串行处理流程,因为每个节点都能独立扩展和插拔。”
- 易错点
- 责任链不等于所有流程都该拆成链。链路太长会让问题定位和执行开销变差。
4.5 其他行为型 ★
- 迭代器模式(Iterator)
- 核心结论:封装集合遍历细节。
- 状态模式(State)
- 核心结论:把状态切换和状态行为显式建模,适合订单流转、审批流。
- 命令模式(Command)
- 核心结论:把请求封装成对象,便于排队、记录、回放。
- 中介者模式(Mediator)
- 核心结论:通过中心对象协调交互,减少对象之间网状依赖。
5. 框架中的设计模式 ★★★
5.1 Spring中的设计模式
| 模式 | 应用 |
|---|---|
| 工厂 | BeanFactory、FactoryBean |
| 单例 | 默认 Bean 作用域 |
| 代理 | AOP(JDK/CGLIB) |
| 模板方法 | JdbcTemplate、RestTemplate |
| 观察者 | ApplicationEvent |
| 适配器 | HandlerAdapter |
| 策略 | 多实现注入、资源加载策略 |
| 责任链 | Filter / Interceptor 链 |
- 面试怎么答
- “Spring 最值得讲的不是某一个模式,而是 IOC、AOP、模板、事件机制这些模式如何协同形成一整套扩展框架。”
5.2 MyBatis中的设计模式
- 建造者:
SqlSessionFactoryBuilder - 工厂:
SqlSessionFactory - 代理:Mapper 接口的 JDK 动态代理
- 模板方法:
BaseExecutor
6. DDD领域驱动设计补充 ★★★(P8+)
6.1 分层架构演进
- 核心结论
- 从传统三层走向 DDD 四层,不是为了“架构更高级”,而是为了让核心业务规则从基础设施细节中解耦。
- 原理展开
- UI 层负责交互和接口适配。
- Application 层负责用例编排,应该保持薄。
- Domain 层承载业务规则和领域行为。
- Infrastructure 层负责数据库、RPC、MQ、缓存等技术实现。
- 面试怎么答
- “DDD 四层最关键的是依赖方向,Domain 不依赖外层具体实现,这样业务规则才能长期稳定演进。”
6.2 贫血模型 vs 充血模型 ★★★
- 核心结论
- 贫血模型适合简单 CRUD,充血模型更适合规则复杂、约束强的核心域。
- 原理展开
- 贫血模型把业务逻辑放在 Service,Entity 只做数据载体。
- 充血模型把规则校验、状态变更、领域不变量收回到实体或聚合内部。
- 面试怎么答
- “不是所有系统都要强行充血。更现实的做法是核心域逐步充血,边缘 CRUD 模块保持贫血。”
- 易错点
- 充血模型不等于把所有 Service 代码搬到 Entity 里。聚合边界和职责仍要清晰。
6.3 防腐层(Anti-Corruption Layer) ★★
- 核心结论
- 防腐层的职责是阻断外部模型和概念污染内部领域,不只是做字段映射。
- 原理展开
- 常见落地是接口隔离 + 适配器 + DTO/VO 转换 + 异常语义统一。
- 面试怎么答
- “外部系统随时可能变,防腐层就是在边界处把变化拦住,让内部模型保持稳定。”
二、高频面试题
基础级(P7必答)
- 单例模式有几种实现?为什么推荐枚举单例?DCL 为什么需要
volatile?
- 30秒答法
- 常见实现有饿汉、懒汉、DCL、静态内部类和枚举。枚举单例最稳,因为天然防反射和反序列化破坏。DCL 里的
volatile关键是禁止重排序,避免读到半初始化对象。
- 常见实现有饿汉、懒汉、DCL、静态内部类和枚举。枚举单例最稳,因为天然防反射和反序列化破坏。DCL 里的
- 关键词
- 枚举单例
- 反序列化
- 指令重排
volatile
- 追问提醒
- 常追问静态内部类为什么线程安全、Spring 单例和 GoF 单例有什么区别。
- JDK 动态代理和 CGLIB 的区别?Spring AOP 用的哪个?
- 30秒答法
- JDK 动态代理基于接口,CGLIB 基于继承。前者要求目标类有接口,后者不能代理
final类和final方法。Spring 会根据目标类型和配置选择代理方式,现代项目里常统一偏向 CGLIB。
- JDK 动态代理基于接口,CGLIB 基于继承。前者要求目标类有接口,后者不能代理
- 关键词
InvocationHandlerMethodInterceptor- 接口代理
- 继承代理
- 追问提醒
- 继续准备回答为什么自调用会导致 AOP 失效。
- 策略模式如何消除
if-else?在你的项目中怎么用的?
- 30秒答法
- 把每种分支规则封装成独立策略,实现统一接口,再通过工厂或容器按 key 路由选择。这样新增渠道只需要加新策略,不需要反复修改主流程。
- 关键词
- 策略接口
- 注册表
- key 路由
- 扩展点
- 追问提醒
- 常追问策略和工厂怎么配合、分支少时还要不要用策略。
- 工厂模式的几种形式?Spring 中怎么体现的?
- 30秒答法
- 简单工厂适合统一创建入口,工厂方法适合扩展单个产品,抽象工厂适合扩展一整组产品族。Spring 里
BeanFactory是工厂机制核心,FactoryBean则是把复杂 Bean 的创建过程也工厂化。
- 简单工厂适合统一创建入口,工厂方法适合扩展单个产品,抽象工厂适合扩展一整组产品族。Spring 里
- 关键词
- 简单工厂
- 工厂方法
- 抽象工厂
FactoryBean
- 追问提醒
- 常继续问
BeanFactory和ApplicationContext的关系。
- 常继续问
- 说说你在项目中用过的设计模式?解决了什么问题?
- 30秒答法
- 最稳的回答方式是按“场景问题 -> 选了什么模式 -> 为什么这样选 -> 收益”来讲。比如策略 + 工厂消除支付渠道分支,模板方法统一任务执行框架,责任链处理风控校验,观察者做异步通知解耦。
- 关键词
- 场景驱动
- 选型理由
- 收益
- 权衡
- 追问提醒
- 面试官通常会追问你为什么不用别的模式。
- SOLID 原则分别是什么?举例说明开闭原则?
- 30秒答法
- SOLID 分别是单一职责、开闭、里氏替换、接口隔离、依赖倒置。开闭原则的典型例子是新增支付方式时只增加新的策略实现,不改原有支付编排逻辑。
- 关键词
- SRP
- OCP
- LSP
- DIP
- 追问提醒
- 继续准备“过度设计是不是违反 YAGNI”这类追问。
- 观察者模式和事件驱动架构有什么关系?在项目中怎么用的?
- 30秒答法
- 观察者模式是一对多通知的基础模型,事件驱动架构则把这种思想扩展到系统层面。项目里常用它处理支付成功、订单状态变更、消息通知这类异步扩展场景。
- 关键词
- 发布订阅
- 领域事件
- 异步解耦
- 幂等
- 追问提醒
- 常追问同步事件和 MQ 异步事件如何选择。
- 责任链模式在项目中怎么应用?和 Filter 链有什么关系?
- 30秒答法
- 责任链是把多个处理节点串起来依次执行,Filter 链就是它的典型实现。项目里它特别适合做鉴权、校验、风控、限流等可插拔流程。
- 关键词
- 链式处理
- Filter
- Interceptor
- 可插拔
- 追问提醒
- 常继续问如何中断链路、如何保证执行顺序和观测性。
- 模板方法模式和策略模式怎么选?区别是什么?
- 30秒答法
- 模板方法适合流程骨架稳定、局部步骤可变的场景,靠继承实现;策略模式适合整体算法可替换的场景,靠组合实现。前者强调约束流程,后者强调运行时切换。
- 关键词
- 继承
- 组合
- 流程骨架
- 算法替换
- 追问提醒
- 面试官常要求你结合实际业务举例,而不是只讲定义。
- 建造者模式的链式调用和 Director 模式有什么区别?
- 30秒答法
- 链式 Builder 更灵活,适合多数业务对象构建;Director 适合构建顺序固定、步骤受约束的复杂对象。简单对象用链式即可,复杂装配再考虑 Director。
- 关键词
- Fluent Builder
- Director
- 构建顺序
- 校验
- 追问提醒
- 可以顺带补充不可变对象和 Builder 的关系。
- 适配器模式在实际项目中有什么应用?类适配和对象适配怎么选?
- 30秒答法
- 适配器常用于新老系统接口不兼容、外部 SDK 接入、领域模型隔离。实际项目里更推荐对象适配器,因为组合更灵活,也不会受单继承限制。
- 关键词
- 新老系统兼容
- 对象适配器
- 组合
- 边界隔离
- 追问提醒
- 常追问防腐层和适配器的关系。
进阶级(P8+深挖)
- 贫血模型和充血模型的区别?你的项目用的是哪种?为什么?
- 30秒答法
- 贫血模型把逻辑放在 Service,适合简单 CRUD;充血模型把规则收回领域对象,适合复杂核心域。更现实的做法是核心模块逐步充血,边缘模块保持轻量。
- 关键词
- 贫血
- 充血
- 领域规则
- 核心域
- 追问提醒
- 常追问聚合根、领域服务和应用服务怎么划分。
- 如何从传统三层架构演进到 DDD 四层架构?
- 30秒答法
- 先从最复杂的核心模块开始,把业务规则从 Service 下沉到 Domain,再用 Application 层做用例编排,Infrastructure 实现技术细节。关键不是一次性重构,而是先把依赖方向理顺。
- 关键词
- 渐进式演进
- Domain
- Application
- 依赖倒置
- 追问提醒
- 常继续问 Repository 接口放哪层、事务边界怎么定。
- 你怎么判断该用继承还是组合?举实际例子。
- 30秒答法
- 默认优先组合,因为更灵活、边界更清晰。只有明确
is-a且不会破坏父类契约时才用继承。比如BufferedInputStream更适合组合装饰,而不是继承一层层堆能力。
- 默认优先组合,因为更灵活、边界更清晰。只有明确
- 关键词
is-a- 组合优先
- LSP
- 装饰器
- 追问提醒
- 准备一个你项目里“曾经错误继承、后面改成组合”的例子会更有说服力。
- 设计一个可扩展的支付系统,用到哪些设计模式?
- 30秒答法
- 支付渠道选择用策略模式,渠道实例创建用工厂,公共流程用模板方法,风控和校验用责任链,支付成功后的通知用观察者或事件驱动。这套组合比单一模式更贴近真实项目。
- 关键词
- 策略
- 工厂
- 模板方法
- 责任链
- 追问提醒
- 常追问幂等、状态机和失败补偿怎么接进来。
- 微服务中的防腐层怎么设计?
- 30秒答法
- 在外部系统边界增加一层适配与转换逻辑,把外部 DTO、错误码、状态语义转换成本域模型,避免外部概念直接侵入内部领域。核心是隔离变化,而不是单纯写几个 converter。
- 关键词
- 防腐层
- 边界
- 适配器
- 模型转换
- 追问提醒
- 可继续准备接口隔离、灰度兼容和测试替身设计。
三、实战场景题(P8+重点)
- 重构遗留代码:接手一个 3000 行的 Service 类,如何重构?用什么模式?
- 回答框架
- 先识别职责边界和变化点,拆出编排、校验、持久化、通知等模块。
- 再决定哪些地方适合策略、模板方法、责任链或防腐层。
- 最后通过测试兜底、灰度替换、逐步下沉逻辑,避免一次性重构爆炸。
- 核心关注点
- 先拆职责,再谈模式。
- 模式服务于问题,不是为了把 3000 行代码拆成 30 个类。
- 规则引擎:业务有大量规则判断(几十个
if-else嵌套),如何设计更优雅的方案?
- 回答框架
- 先判断规则是否稳定、是否需要动态配置。
- 静态规则优先考虑策略 + 工厂 + 责任链;动态规则再评估规则引擎或 DSL。
- 加上优先级、短路、灰度和观测能力。
- 核心关注点
- 不要一看到复杂规则就上 Drools。
- 规则复杂度、可运营性和调试成本要一起评估。
- 插件架构:需要设计一个支持插件扩展的系统,如何利用 SPI 和设计模式实现?
- 回答框架
- 先定义稳定扩展点接口,再用 SPI 或注册中心发现插件实现。
- 核心流程可用模板方法,插件选择可用工厂或策略,插件链路可用责任链。
- 补齐隔离、版本兼容、生命周期和异常兜底。
- 核心关注点
- 扩展点接口必须稳定。
- 插件失效不能拖垮主流程。
- 领域建模:对一个电商订单系统进行领域建模,识别聚合、实体、值对象。
- 回答框架
- 先按业务能力切子域,再识别订单、支付、履约、库存等核心对象。
- 明确聚合根和一致性边界,比如订单作为聚合根统一控制状态流转。
- 区分实体和值对象,并把状态变更规则收回聚合内部。
- 核心关注点
- 不要把数据库表直接等同为领域对象。
- 聚合边界要围绕一致性而不是围绕页面模块。
四、学习资源推荐
书籍
- 《Head First 设计模式》:适合快速建立模式直觉。
- 《设计模式:可复用面向对象软件的基础》:经典原著,适合查定义。
- 《重构:改善既有代码的设计》:更贴近工程落地。
- 《领域驱动设计》:适合理解模式和领域建模的结合。
博客/文章
- Refactoring.Guru:图示和意图解释清晰。
- 极客时间《设计模式之美》:更偏工程实战。
- 极客时间《DDD 实战课》:适合从模式走向领域建模。
视频
- 尚硅谷图解 Java 设计模式:适合快速扫盲。
- B 站设计模式实战类课程:适合配合项目案例理解。