跳转至

02-创建型模式

创建型模式分类图

学习时间: 约4-5小时 难度级别: ⭐⭐ 初中级 前置知识: SOLID设计原则、面向对象编程 学习目标: 掌握5种创建型模式的意图、结构、实现和适用场景,能在实际项目中灵活运用


🎯 学习目标

  1. 理解创建型模式解决的核心问题:将对象的创建使用分离
  2. 掌握单例模式的多种实现方式和线程安全问题
  3. 理解工厂方法与抽象工厂的区别和适用场景
  4. 掌握建造者模式的链式调用设计
  5. 理解原型模式中浅拷贝与深拷贝的区别

目录


1. 创建型模式概述

创建型模式关注对象的创建方式,核心目标是将对象的创建逻辑与使用逻辑解耦,使系统在"创建什么对象"和"如何创建对象"方面更加灵活。

模式 解决的问题 一句话
单例 确保全局唯一实例 "只能有一个"
工厂方法 让子类决定创建哪类对象 "创建交给子类"
抽象工厂 创建一族相关对象 "产品家族"
建造者 分步构建复杂对象 "一步一步来"
原型 通过复制创建新对象 "克隆一个"

2. 单例模式(Singleton)

意图

确保一个类只有一个实例,并提供全局访问点。

适用场景

  • 数据库连接池、线程池
  • 配置管理器(加载一次,全局使用)
  • 日志管理器
  • 缓存管理器

结构(文字描述UML)

Text Only
┌──────────────┐
│  Singleton    │
├──────────────┤
│ -instance    │ ← 静态私有实例
├──────────────┤
│ -__init__()  │ ← 私有构造函数
│ +getInstance()│ ← 公有静态方法
│ +operation() │
└──────────────┘

Python实现

方式一:模块天然单例(最Pythonic)

Python的模块在第一次导入时执行,之后从缓存中获取,天然就是单例。

Python
# config.py — 模块本身就是单例
class _Config:
    def __init__(self):
        self.debug = False
        self.db_url = "sqlite:///app.db"

    def load(self, path):
        # 从文件加载配置
        ...

config = _Config()  # 全局唯一实例

# 使用:from config import config

方式二:使用 __new__ (经典方式)

Python
# ⚠️ 此实现非线程安全:多线程并发调用时,两个线程可能同时通过 `is None` 检查,
# 从而创建多个实例。如需线程安全,请参考下方「方式四:线程安全的单例」
# 或直接使用模块级实例(方式一)。
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        # 注意:__init__每次调用都会执行
        if not hasattr(self, '_initialized'):
            self._initialized = True
            self.data = {}

# 测试
a = Singleton()
b = Singleton()
assert a is b  # ✅ 同一个实例

方式三:装饰器方式

Python
def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Database:
    def __init__(self):
        self.connection = "Connected!"

方式四:线程安全的单例

Python
import threading  # 线程池/多线程:并发执行任务

class ThreadSafeSingleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):  # *args接收任意位置参数;**kwargs接收任意关键字参数
        if cls._instance is None:  # 第一次检查(无锁,性能优化)
            with cls._lock:
                if cls._instance is None:  # 第二次检查(加锁后,保证安全)
                    cls._instance = super().__new__(cls)  # super()调用父类方法
        return cls._instance

Java实现

Java
// 方式一:饿汉式(类加载时创建,线程安全)
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}

// 方式二:双重检查锁(懒汉式,线程安全)
public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {  // synchronized同步锁,保证线程安全
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

// 方式三:枚举(最佳实践,防反射和序列化攻击)
public enum Singleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Singleton operation");
    }
}

优缺点

优点 缺点
保证全局唯一实例 违反单一职责(管理自身生命周期)
延迟初始化节省资源 难以进行单元测试(全局状态)
全局访问点 隐藏了类之间的依赖关系

实际应用

  • Python的 logging.getLogger(name) — 同名logger返回同一实例
  • Python的 None, True, False — 天然单例
  • Java的 Runtime.getRuntime()
  • Spring框架的Bean默认是单例

3. 工厂方法模式(Factory Method)

意图

定义一个创建对象的接口,让子类决定实例化哪个类。

适用场景

  • 不确定需要创建什么具体类的对象
  • 希望把对象创建的责任委托给子类
  • 框架中由用户决定具体产品(如日志框架)

结构

Text Only
┌───────────────┐        ┌───────────────┐
│   Creator     │        │   Product     │
│  (abstract)   │        │  (abstract)   │
├───────────────┤        ├───────────────┤
│ +factoryMethod()│─创建→│ +operation()  │
│ +someOperation()│      └───────┬───────┘
└───────┬───────┘                │
        │                        │
┌───────┴───────┐        ┌──────┴────────┐
│ConcreteCreator│        │ConcreteProduct│
├───────────────┤        ├───────────────┤
│ +factoryMethod()│       │ +operation()  │
└───────────────┘        └───────────────┘

Python实现

Python
from abc import ABC, abstractmethod

# 产品接口
class Notification(ABC):
    @abstractmethod
    def send(self, message: str) -> None:
        pass

# 具体产品
class EmailNotification(Notification):
    def send(self, message):
        print(f"📧 Email: {message}")

class SMSNotification(Notification):
    def send(self, message):
        print(f"📱 SMS: {message}")

class PushNotification(Notification):
    def send(self, message):
        print(f"🔔 Push: {message}")

# 工厂接口
class NotificationFactory(ABC):
    @abstractmethod
    def create_notification(self) -> Notification:
        pass

    def notify(self, message):
        """模板方法:使用工厂方法创建对象并发送通知"""
        notification = self.create_notification()
        notification.send(message)

# 具体工厂
class EmailFactory(NotificationFactory):
    def create_notification(self):
        return EmailNotification()

class SMSFactory(NotificationFactory):
    def create_notification(self):
        return SMSNotification()

# 简单工厂(变体:集中创建逻辑)
class SimpleNotificationFactory:
    @staticmethod  # @staticmethod静态方法,不需要实例
    def create(channel: str) -> Notification:
        factories = {
            "email": EmailNotification,
            "sms": SMSNotification,
            "push": PushNotification,
        }
        if channel not in factories:
            raise ValueError(f"Unknown channel: {channel}")
        return factories[channel]()

# 使用
factory = EmailFactory()
factory.notify("Your order has been shipped!")

notification = SimpleNotificationFactory.create("push")
notification.send("New message received!")

Java实现

Java
// 产品接口
public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) {
        System.out.println("📧 Email: " + message);
    }
}

// 工厂接口
public abstract class NotificationFactory {
    public abstract Notification createNotification();

    public void notify(String message) {
        Notification notification = createNotification();
        notification.send(message);
    }
}

public class EmailFactory extends NotificationFactory {
    public Notification createNotification() {
        return new EmailNotification();
    }
}

优缺点

优点 缺点
解耦创建与使用 每增加一种产品需要新建一对类
遵循开闭原则 简单场景可能过度设计
遵循单一职责 类的数量增多

4. 抽象工厂模式(Abstract Factory)

意图

提供一个创建一系列相关对象的接口,无需指定它们的具体类。

适用场景

  • 系统需要独立于产品的创建和组合
  • 系统需要配置多个"产品族"(如不同操作系统的UI组件)
  • 需要确保同族产品一起使用

Python实现

Python
from abc import ABC, abstractmethod

# ====== 抽象产品 ======
class Button(ABC):
    @abstractmethod
    def render(self): pass

class TextBox(ABC):
    @abstractmethod
    def render(self): pass

# ====== Windows产品族 ======
class WindowsButton(Button):
    def render(self):
        return "[Windows Button]"

class WindowsTextBox(TextBox):
    def render(self):
        return "[Windows TextBox]"

# ====== macOS产品族 ======
class MacButton(Button):
    def render(self):
        return "(Mac Button)"

class MacTextBox(TextBox):
    def render(self):
        return "(Mac TextBox)"

# ====== 抽象工厂 ======
class UIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: pass

    @abstractmethod
    def create_textbox(self) -> TextBox: pass

class WindowsFactory(UIFactory):
    def create_button(self): return WindowsButton()
    def create_textbox(self): return WindowsTextBox()

class MacFactory(UIFactory):
    def create_button(self): return MacButton()
    def create_textbox(self): return MacTextBox()

# ====== 客户端代码 ======
def create_ui(factory: UIFactory):
    """客户端只依赖抽象工厂,不关心具体产品族"""
    button = factory.create_button()
    textbox = factory.create_textbox()
    print(button.render(), textbox.render())

# 根据运行环境选择工厂
import platform
factory = WindowsFactory() if platform.system() == "Windows" else MacFactory()
create_ui(factory)

Java实现

Java
public interface UIFactory {  // interface定义类型契约
    Button createButton();
    TextBox createTextBox();
}

public class WindowsFactory implements UIFactory {
    public Button createButton() { return new WindowsButton(); }
    public TextBox createTextBox() { return new WindowsTextBox(); }
}

优缺点

优点 缺点
保证产品族一致性 增加新产品类型困难(需修改所有工厂)
客户端与具体产品解耦 类的数量爆炸式增长
遵循开闭原则(新增产品族) 代码复杂度高

实际应用

  • Java AWT / Swing的跨平台UI组件
  • 数据库驱动(JDBC的Connection、Statement、ResultSet是一族)
  • 游戏引擎中不同平台的渲染器、音频引擎

5. 建造者模式(Builder)

意图

将一个复杂对象的构建过程与其表示分离,使同样的构建过程可以创建不同的表示。

适用场景

  • 对象有很多可选参数(避免"望远镜构造函数"反模式)
  • 对象创建有多个步骤,步骤顺序可能不同
  • 需要创建同一对象的不同变体

Python实现

Python
class Computer:
    """产品:电脑"""
    def __init__(self):
        self.cpu = None
        self.ram = None
        self.storage = None
        self.gpu = None
        self.os = None

    def __str__(self):
        parts = [f"CPU: {self.cpu}", f"RAM: {self.ram}", f"Storage: {self.storage}"]
        if self.gpu:
            parts.append(f"GPU: {self.gpu}")
        if self.os:
            parts.append(f"OS: {self.os}")
        return " | ".join(parts)

class ComputerBuilder:
    """建造者:链式调用风格"""
    def __init__(self):
        self._computer = Computer()

    def cpu(self, cpu: str) -> 'ComputerBuilder':
        self._computer.cpu = cpu
        return self  # 返回self实现链式调用

    def ram(self, ram: str) -> 'ComputerBuilder':
        self._computer.ram = ram
        return self

    def storage(self, storage: str) -> 'ComputerBuilder':
        self._computer.storage = storage
        return self

    def gpu(self, gpu: str) -> 'ComputerBuilder':
        self._computer.gpu = gpu
        return self

    def os(self, os: str) -> 'ComputerBuilder':
        self._computer.os = os
        return self

    def build(self) -> Computer:
        """构建最终产品"""
        computer = self._computer
        self._computer = Computer()  # 重置,以便复用Builder
        return computer

# 使用:链式调用,清晰易读
gaming_pc = (ComputerBuilder()
    .cpu("Intel i9-13900K")
    .ram("32GB DDR5")
    .storage("2TB NVMe SSD")
    .gpu("RTX 4090")
    .os("Windows 11")
    .build())

office_pc = (ComputerBuilder()
    .cpu("Intel i5-13400")
    .ram("16GB DDR4")
    .storage("512GB SSD")
    .os("Ubuntu 22.04")
    .build())

print(gaming_pc)
print(office_pc)

Python进阶:使用 dataclass 简化

Python
from dataclasses import dataclass, field

@dataclass  # 自动生成__init__等方法
class ServerConfig:
    """Python中dataclass + 默认值常可替代Builder"""
    host: str = "localhost"
    port: int = 8080
    workers: int = 4
    debug: bool = False
    timeout: int = 30
    ssl: bool = False
    cert_path: str = ""

# 使用:命名参数即可
config = ServerConfig(host="0.0.0.0", port=443, ssl=True, cert_path="/etc/ssl/cert.pem")

Java实现

Java
public class Computer {
    private final String cpu;  // 必选
    private final String ram;  // 必选
    private String gpu;        // 可选
    private String os;         // 可选

    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.gpu = builder.gpu;
        this.os = builder.os;
    }

    public static class Builder {
        private final String cpu;
        private final String ram;
        private String gpu;
        private String os;

        public Builder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

        public Builder gpu(String gpu) { this.gpu = gpu; return this; }
        public Builder os(String os) { this.os = os; return this; }

        public Computer build() { return new Computer(this); }
    }
}

// 使用
Computer pc = new Computer.Builder("i9", "32GB")
    .gpu("RTX 4090")
    .os("Windows")
    .build();

优缺点

优点 缺点
避免构造函数参数过多 代码量增加(需要额外的Builder类)
步骤清晰,代码易读 简单对象用Builder是过度设计
可以创建不可变对象

实际应用

  • Java的 StringBuilder
  • OkHttp的 Request.Builder()
  • Lombok的 @Builder 注解
  • SQL查询构建器

6. 原型模式(Prototype)

意图

通过复制已有对象来创建新对象,避免重复初始化的开销。

适用场景

  • 对象创建代价大(如涉及数据库查询、网络请求)
  • 需要创建与已有对象相似的新对象
  • 需要在运行时动态指定产品类型

浅拷贝 vs 深拷贝

Python
import copy

class Address:
    def __init__(self, city, street):
        self.city = city
        self.street = street

class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address  # 引用类型

    def clone(self):
        """浅拷贝:内部的引用对象仍指向同一实例"""
        return copy.copy(self)

    def deep_clone(self):
        """深拷贝:完全独立的副本"""
        return copy.deepcopy(self)

# 演示区别
original = Person("Alice", 30, Address("Beijing", "Main St"))

# 浅拷贝
shallow = original.clone()
shallow.name = "Bob"              # 修改值类型 → 不影响原对象 ✅
shallow.address.city = "Shanghai" # 修改引用类型 → 影响原对象!❌
print(original.address.city)  # "Shanghai" — 被修改了!

# 深拷贝
deep = original.deep_clone()
deep.address.city = "Shenzhen"
print(original.address.city)  # "Shanghai" — 不受影响 ✅

Python实现

Python
from abc import ABC, abstractmethod  # ABC抽象基类;abstractmethod强制子类实现
import copy

class Prototype(ABC):
    @abstractmethod
    def clone(self):
        pass

class GameCharacter(Prototype):
    def __init__(self, name, level, skills, equipment):
        self.name = name
        self.level = level
        self.skills = skills      # list
        self.equipment = equipment  # dict

    def clone(self):
        return copy.deepcopy(self)

    def __str__(self):
        return f"{self.name} (Lv.{self.level}) Skills:{self.skills}"

# 原型注册表
class CharacterRegistry:
    def __init__(self):
        self._prototypes = {}

    def register(self, name, prototype):
        self._prototypes[name] = prototype

    def create(self, name, **overrides):
        prototype = self._prototypes.get(name)
        if prototype is None:
            raise ValueError(f"Unknown prototype: {name}")
        clone = prototype.clone()
        for key, value in overrides.items():
            setattr(clone, key, value)  # hasattr/getattr/setattr动态操作对象属性
        return clone

# 注册原型
registry = CharacterRegistry()
registry.register("warrior", GameCharacter(
    "Warrior", 1, ["Slash", "Block"], {"weapon": "Sword", "armor": "Shield"}
))
registry.register("mage", GameCharacter(
    "Mage", 1, ["Fireball", "Heal"], {"weapon": "Staff", "armor": "Robe"}
))

# 基于原型创建新角色
player1 = registry.create("warrior", name="Arthur", level=10)
player2 = registry.create("mage", name="Merlin", level=8)
print(player1)  # Arthur (Lv.10) Skills:['Slash', 'Block']
print(player2)  # Merlin (Lv.8) Skills:['Fireball', 'Heal']

Java实现

Java
public class GameCharacter implements Cloneable {  // extends继承;implements实现接口
    private String name;
    private int level;
    private List<String> skills;

    @Override  // @Override重写父类方法
    public GameCharacter clone() {
        try {  // try/catch捕获异常
            GameCharacter clone = (GameCharacter) super.clone();
            clone.skills = new ArrayList<>(this.skills);  // 深拷贝集合
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

优缺点

优点 缺点
减少重复初始化开销 深拷贝复杂对象可能很昂贵
动态决定产品类型 循环引用的深拷贝需小心
简化创建过程 需要为每个类实现clone方法

实际应用

  • Python的 copy.copy() / copy.deepcopy()
  • JavaScript的 Object.assign() / spread operator
  • Java的 Cloneable 接口
  • 游戏中从"模板"创建大量相似的NPC/敌人

7. 创建型模式对比

模式 核心问题 创建方式 产品类型
单例 限制实例数量 类自身控制 单一类
工厂方法 推迟到子类创建 继承 + 多态 单一产品
抽象工厂 创建一族产品 组合 + 多态 产品族
建造者 分步构建复杂对象 步骤化构建 复杂对象
原型 通过复制创建 克隆已有实例 任意类型

选择指南: - 只需要一个实例 → 单例 - 创建过程简单但类型不确定 → 工厂方法 - 需要创建一组配套的对象 → 抽象工厂 - 对象很复杂、有很多可选配置 → 建造者 - 需要创建已有对象的变体 → 原型


8. 练习与自我检查

✏️ 练习题

  1. 实现线程安全单例:用Python实现一个线程安全的数据库连接池单例类,支持 get_connection()release_connection() 方法。

  2. 工厂方法实践:设计一个日志系统,支持输出到控制台、文件、远程服务器。使用工厂方法模式,让新增日志目标不需要修改已有代码。

  3. 抽象工厂应用:设计一个跨数据库ORM,使用抽象工厂创建不同数据库(MySQL、PostgreSQL、SQLite)的Connection、Cursor、Query对象。

  4. 建造者实践:为HTTP请求构建一个Builder,支持设置URL、Method、Headers、Body、Timeout等参数,链式调用。

  5. 原型模式应用:在一个游戏中,基于"兵种模板"克隆大量士兵(步兵、弓兵、骑兵),每个士兵有独立的HP和位置但共享基础属性。

  6. 模式选择:以下场景分别应该使用哪种创建型模式?

  7. 全局配置管理器
  8. 根据用户输入创建不同的图表类型(柱状图/折线图/饼图)
  9. 构建包含多个可选字段的SQL查询
  10. 根据操作系统创建对应的文件对话框+菜单栏+工具栏

面试要点

Q1: 实现单例模式有哪些方式?各有什么优缺点? A: 饿汉式(类加载时创建,线程安全但不能延迟加载)、懒汉式+双重检查锁(延迟加载,线程安全)、枚举(Java最佳,防反射和序列化)、模块天然单例(Python最佳)。

Q2: 工厂方法和抽象工厂的区别? A: 工厂方法创建一种产品,通过继承让子类决定具体类型;抽象工厂创建一族相关产品,保证产品族的一致性。抽象工厂中每个方法实际上就是一个工厂方法。

Q3: 什么时候用建造者模式而不是构造函数? A: 当对象有4个以上的参数(尤其是可选参数多),或者构建过程有多个步骤,或者需要构造不可变对象时。Java中尤其推荐(Python可以用dataclass+默认参数替代简单场景)。

Q4: 深拷贝和浅拷贝的区别? A: 浅拷贝只复制对象本身和其直接属性的值(引用类型只复制引用,共享底层对象);深拷贝递归复制所有层次的对象,得到完全独立的副本。

自我检查清单

  • 能用至少两种方式实现Python单例
  • 理解Java中枚举单例为什么是最佳实践
  • 能区分简单工厂、工厂方法、抽象工厂
  • 能实现链式调用的Builder模式
  • 理解浅拷贝和深拷贝的区别,能举例说明
  • 能根据场景选择合适的创建型模式

下一章: 03-结构型模式 — 学习如何优雅地组合对象