设计模式
校招考察频度:☆ 中频(原理扩展·工程加分项) 复习主线:设计模式不是死记硬背 23 种类图,而是掌握应对需求变更的代码解耦套路。校招面试核心聚焦点:SOLID 原则在 Python/Java 中的具象落地、经典模式演化(单例模式的多种实现与线程安全 DCL、装饰器与动态代理 AOP 拦截、责任链与 Web 中间件洋葱模型、工厂模式与控制反转依赖注入 DI)。
设计模式是解决软件设计重复问题的模板套路。在 Python 与 Java 双栈视角下,许多重型设计模式在动态语言中退化为简洁的一等公民函数或内置协议,而在 Java 企业级框架中构成了坚实的核心底座。
分类考频地图
| 专题 | 核心页面 | 校招考频星级 | 重点考查要点 |
|---|---|---|---|
| 单例与作用域 | 单例与依赖作用域 | ★ 高频(核心必背) | Python 模块单例与 __new__、Java 双重检查锁(DCL + volatile 禁指令重排)与枚举单例、Spring 单例 Bean 作用域 |
| 策略与模板方法 | 策略与模板方法 | ★ 高频(消除分支) | 消除多重 if-else 坏味道、策略模式依赖注入不同算法、模板方法定义骨架留出钩子(Hook) |
| 依赖注入与工厂 | 依赖注入与工厂 | ☆ 中频(设计思想) | 简单工厂/工厂方法/抽象工厂对比、控制反转(IoC)与依赖注入(DI)、FastAPI Depends 与 Spring @Autowired |
| 装饰器与代理 | 装饰器与代理模式 | ☆ 中频(切面编程) | 装饰器解决横切关注点(日志/限流/鉴权)、动态代理(JDK Proxy 接口代理 vs CGLIB 字节码子类代理)、AOP 核心机制 |
| 中间件与责任链 | 中间件与责任链 | ☆ 中频(管道处理) | 责任链解耦请求发送者与接收者、Web 中间件洋葱模型(Onion Architecture)的递归调用与异常传递 |
| 事件驱动与观察者 | 事件与生命周期 | 低频(事件拓展) | 发布-订阅 vs 观察者、FastAPI lifespan 生命周期上下文、Spring ApplicationEvent 事件监听机制 |
哪些模式”消失”了
| 模式 | Java 的写法 | Python 的写法 |
|---|---|---|
| 单例 | 私有构造 + getInstance | 模块天然单例,import 即复用 |
| 工厂 | 工厂类 + 接口 | 一个函数返回对象 |
| 策略 | 策略接口 + 实现类 | 直接传函数 |
| 观察者 | 观察者接口 + 注册 | 事件回调、信号 |
| 迭代器 | Iterator 接口 | __iter__/__next__ 或直接 for |
原因:Java 的类型系统要求”接口”才能多态,Python 的鸭子类型让任何对象都能扮演角色,函数是一等公民让”行为注入”变成传参。
设计原则在 Python 的体现
SOLID 不是 Java 专属,Python 的落地方式不同:
| 原则 | Python 实践 |
|---|---|
| 单一职责 | 模块和函数小而专注,一个函数只做一件事 |
| 开闭原则 | 协议(Protocol)和鸭子类型扩展行为,不改旧代码 |
| 里氏替换 | 继承谨慎,子类不应破坏父类契约;优先组合 |
| 接口隔离 | Protocol 按需声明,不用继承大接口 |
| 依赖倒置 | 依赖抽象(Protocol/ABC)而不是具体类,靠 DI 注入 |
ABC 与 Protocol 的选择(高频考点):
abc.ABC抽象基类:名义子类,必须显式继承,适合”定义骨架”(模板方法)typing.Protocol:结构化子类,只要方法签名对上就满足,不强制继承,适合”描述能力”- 一句话:Protocol 是鸭子类型的类型标注版,ABC 是传统继承的抽象
组合优于继承
Python 里这个原则格外重要:多重继承有 MRO 坑(菱形继承、super 顺序),组合(持有对象)比继承(成为对象)更好改。dataclasses、依赖注入、装饰器都是组合思维。
面试怎么答
- 先答模式意图(解决什么问题),再答 Python 形态
- 主动说”这个模式在 Python 里通常不这么写”,展示语言意识
- 结合 FastAPI 举例(DI、中间件、lru_cache)比背 Gof 例子有说服力
- 被问”工厂模式”时说函数工厂,被问”单例”时说模块单例加 lru_cache
面试追问
- Python 还需要设计模式吗? 需要,但形态变了。行为型模式多退化为传函数,创建型多退化为模块和函数,结构型(装饰器、代理)仍有独立价值
- ABC 和 Protocol 的区别? ABC 要显式继承(名义子类),Protocol 只要结构匹配(结构化子类)。鸭子类型的类型标注用 Protocol
- 为什么组合优于继承? 继承耦合父类实现,MRO 和菱形继承有坑;组合只依赖接口,便于替换和测试
- 单例在 Python 怎么写? 模块级对象就是单例。需要惰性初始化或参数化时用 lru_cache 或显式工厂
- 设计模式和 Python 惯例冲突怎么办? 优先惯例。Python 里”Pythonic”的解法往往就是模式的简化形态