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__ 拦截)、提供全局访问入口、保证并发下也只创建一个。

⚠️ 常见错误

  1. 单例带来全局状态,让代码难测试、难并行,别滥用——"只需要一个"不等于"必须用单例"。
  2. 多线程下不加同步地 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()                 # 子类决定造什么

好处:新增一种产品,只需新增一个产品类和对应的工厂子类,主流程不用改,符合开闭原则。

⚠️ 常见错误

  1. 产品只有一种、也没有变化趋势时硬套工厂,属于过度设计。
  2. 工厂方法的关键是"创建逻辑在子类",如果创建逻辑全堆在父类的一堆 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 配套使用)。

⚠️ 常见错误

  1. 产品之间其实没有关联,却强行打包进一个抽象工厂,反而制造伪约束。
  2. 抽象工厂有个固有代价:要新增一种产品(比如"加一条围巾"),所有具体工厂都要跟着改——使用前要权衡这个成本。

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()

与工厂的区别:工厂"一步到位"给成品;建造者"分步组装",可以跳过可选部分、控制步骤顺序,适合参数多且部分可选的对象。

⚠️ 常见错误

  1. 对象只有两三个参数时也套建造者,属于过度设计。
  2. 建造过程直接操作产品的内部字段,把封装破坏了——应通过产品提供的接口设置,而不是在外部乱动私有数据。

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)             # 原对象不受影响

适用场景:对象构造昂贵(大量计算、重复读库)时,用克隆代替重新构造。注意深拷贝与浅拷贝:浅拷贝只复制一层,对象里的列表等引用类型会被共享,改副本可能连累原件。

⚠️ 常见错误

  1. 浅拷贝复制了引用类型字段,改副本连累原件(两个对象共用同一个列表)。
  2. 原型对象持有文件句柄、网络连接等资源时,盲目克隆可能复制了不该共享的资源。

📌 术语对照(中英)

中文 English
单例模式 Singleton
工厂方法 Factory Method
抽象工厂 Abstract Factory
建造者 Builder
原型 Prototype
深拷贝 / 浅拷贝 Deep Copy / Shallow Copy

🎯 复习清单

  • 五种创建型模式各解决什么"创建问题",分别用一个生活例子说明。
  • 工厂方法与抽象工厂的区别:单个产品 vs 一族产品。
  • 建造者与工厂的区别:分步组装 vs 一步到位。
  • 原型模式与浅拷贝/深拷贝的关系,以及为什么浅拷贝会连累原件。
  • 单例模式的使用场景与滥用风险。