SD-02 创建型模式
02 · 创建型模式(Creational Patterns)
📅 预计 60 分钟 | ⭐ = 重要知识点 | 📌 中英术语见文末
2.0 先给直觉:把"创建对象"当成一门手艺
平时写代码,一个 new 就造出对象,有什么好研究的?但现实中,创建对象往往带着麻烦:有的对象必须全局唯一、有的对象构造过程很长、有的要按条件选不同产品、有的想复制一份现成的。创建型模式就是解决"怎么把对象造出来"的五种经典套路:单例、工厂方法、抽象工厂、建造者、原型。
一个简单分法:单例管"数量",工厂管"选谁",建造者管"怎么一步步组装",原型管"复制"。
2.1 单例模式(Singleton)⭐
全局只允许一个实例
单例的意思是:整个程序运行期间,这个类只有一个实例。想象一个国家的元首——一个任期里只能有一个,不能今天一个明天一个,否则全国上下都得乱套。
适用场景:日志记录器、配置管理器、数据库连接池。这些对象全局共享一份就够了,多份反而浪费资源、互相打架。
class Config:
_instance = None
def __new__(cls):
if cls._instance is None: # 第一次才真正创建
cls._instance = super().__new__(cls)
return cls._instance # 之后每次返回同一个
c1 = Config()
c2 = Config()
print(c1 is c2) # True:同一个对象
实现要点:把构造过程控制住(Python 里用 __new__ 拦截)、提供全局访问入口、保证并发下也只创建一个。
⚠️ 常见错误
- 单例带来全局状态,让代码难测试、难并行,别滥用——"只需要一个"不等于"必须用单例"。
- 多线程下不加同步地
if cls._instance is None可能创建出两个实例;Python 里常用模块级导入(天然单例)或加锁解决。
2.2 工厂方法模式(Factory Method)⭐
让子类决定造哪种产品
工厂方法把"创建哪个产品"的决策推迟到子类。就像餐厅点餐:你只跟服务员说"来一份主食",至于做面还是做炒饭,由后厨(子类)决定。主程序不关心具体类型,只对着统一接口使用产品。
class Food:
def serve(self): ...
class Noodle(Food):
def serve(self): print("端上一碗面")
class Rice(Food):
def serve(self): print("端上一份饭")
class Restaurant: # 抽象工厂类
def order(self) -> Food: ... # 创建逻辑留给子类
def serve_order(self): # 客户端只依赖抽象
food = self.order()
food.serve()
class NoodleRestaurant(Restaurant):
def order(self) -> Food:
return Noodle() # 子类决定造什么
好处:新增一种产品,只需新增一个产品类和对应的工厂子类,主流程不用改,符合开闭原则。
⚠️ 常见错误
- 产品只有一种、也没有变化趋势时硬套工厂,属于过度设计。
- 工厂方法的关键是"创建逻辑在子类",如果创建逻辑全堆在父类的一堆
if-else里,就失去了意义。
2.3 抽象工厂模式(Abstract Factory)⭐
一族产品一起生产
抽象工厂是工厂方法的"家族版":它创建的不是单个产品,而是一族相互关联的产品。像服装品牌:一个品牌同时出外套、裤子、鞋,风格统一;要换风格,就整体换一个品牌。
class Suit: ...
class Pants: ...
class StyleFactory: # 抽象工厂:定义一族的接口
def make_suit(self) -> Suit: ...
def make_pants(self) -> Pants: ...
class CasualFactory(StyleFactory):
def make_suit(self): return CasualSuit()
def make_pants(self): return CasualPants()
class FormalFactory(StyleFactory):
def make_suit(self): return FormalSuit()
def make_pants(self): return FormalPants()
适用场景:UI 主题(暗色/亮色各是一整套控件,颜色风格必须统一)、数据库访问(同一厂商的 Connection、Statement、ResultSet 配套使用)。
⚠️ 常见错误
- 产品之间其实没有关联,却强行打包进一个抽象工厂,反而制造伪约束。
- 抽象工厂有个固有代价:要新增一种产品(比如"加一条围巾"),所有具体工厂都要跟着改——使用前要权衡这个成本。
2.4 建造者模式(Builder)⭐
一步步组装复杂对象
当对象的构造步骤多、参数组合繁杂时,建造者把构造过程拆成一步步,最后统一产出成品。像组装电脑:CPU、内存、硬盘分别选,最后装成整机。比起在构造函数里塞十几个参数(谁是谁都分不清),清晰得多。
class Computer:
def __init__(self):
self.cpu = None; self.ram = None; self.disk = None
class ComputerBuilder:
def __init__(self):
self.computer = Computer()
def cpu(self, c): self.computer.cpu = c; return self # 返回 self,支持链式
def ram(self, r): self.computer.ram = r; return self
def disk(self, d): self.computer.disk = d; return self
def build(self): return self.computer
pc = ComputerBuilder().cpu("i7").ram("16G").disk("1T").build()
与工厂的区别:工厂"一步到位"给成品;建造者"分步组装",可以跳过可选部分、控制步骤顺序,适合参数多且部分可选的对象。
⚠️ 常见错误
- 对象只有两三个参数时也套建造者,属于过度设计。
- 建造过程直接操作产品的内部字段,把封装破坏了——应通过产品提供的接口设置,而不是在外部乱动私有数据。
2.5 原型模式(Prototype)⭐
复制一份已有的模板对象
原型模式通过克隆已有对象来创建新对象,而不是从零构造。像复印身份证:原件在手,复印机一按,立刻得到一份副本,不必重新填写所有信息。
Python 的 copy.deepcopy / copy.copy 就是最常用的原型实现:
import copy
class Report:
def __init__(self, title, sections):
self.title = title
self.sections = sections
base = Report("月度报告", ["概览", "数据", "结论"])
rep = copy.deepcopy(base) # 克隆一份
rep.sections.append("附录") # 改副本
print(base.sections) # 原对象不受影响
适用场景:对象构造昂贵(大量计算、重复读库)时,用克隆代替重新构造。注意深拷贝与浅拷贝:浅拷贝只复制一层,对象里的列表等引用类型会被共享,改副本可能连累原件。
⚠️ 常见错误
- 浅拷贝复制了引用类型字段,改副本连累原件(两个对象共用同一个列表)。
- 原型对象持有文件句柄、网络连接等资源时,盲目克隆可能复制了不该共享的资源。
📌 术语对照(中英)
| 中文 | English |
|---|---|
| 单例模式 | Singleton |
| 工厂方法 | Factory Method |
| 抽象工厂 | Abstract Factory |
| 建造者 | Builder |
| 原型 | Prototype |
| 深拷贝 / 浅拷贝 | Deep Copy / Shallow Copy |
🎯 复习清单
- 五种创建型模式各解决什么"创建问题",分别用一个生活例子说明。
- 工厂方法与抽象工厂的区别:单个产品 vs 一族产品。
- 建造者与工厂的区别:分步组装 vs 一步到位。
- 原型模式与浅拷贝/深拷贝的关系,以及为什么浅拷贝会连累原件。
- 单例模式的使用场景与滥用风险。