设计模式
设计模式是解决重复问题的套路,但套路的形态随语言变化。Python 是动态语言,很多 Java 模式在 Python 里退化为语言特性或几行代码。面试主线的正确姿势:模式的本意是什么,Python 里它长什么样。
哪些模式“消失”了
| 模式 | 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”的解法往往就是模式的简化形态