Files
2026-07-13 21:36:32 +08:00

13 KiB
Raw Permalink Blame History

创建型设计模式

创建型模式处理对象的创建机制,试图以适合当前情境的方式创建对象。


1. 单例模式(Singleton Pattern

问题

你需要一个类的唯一实例(例如,数据库连接、配置管理器、日志记录器)。

反面示例

# 可以创建多个实例
class DatabaseConnection:
    def __init__(self):
        self.connection = self.connect()

    def connect(self):
        print("Connecting to database...")
        return "DB Connection"

# 问题:创建了多个连接
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # False — 不同的实例!

解决方案

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

class DatabaseConnection(Singleton):
    def __init__(self):
        if not hasattr(self, 'initialized'):
            self.connection = self.connect()
            self.initialized = True

    def connect(self):
        print("Connecting to database...")
        return "DB Connection"

# 使用示例
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2)  # True — 同一个实例!

JavaScript 实现

class DatabaseConnection {
    constructor() {
        if (DatabaseConnection.instance) {
            return DatabaseConnection.instance;
        }

        this.connection = this.connect();
        DatabaseConnection.instance = this;
    }

    connect() {
        console.log("Connecting to database...");
        return "DB Connection";
    }
}

// 使用示例
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2);  // true

何时使用

  • 适用:日志记录器、配置管理、连接池、缓存
  • 不适用:当你需要多个实例时,或用于简单工具类(改用模块)

优缺点

对唯一实例的受控访问 延迟初始化(Lazy initialization 全局状态(可能增加测试难度) 可能违反单一职责原则(Single Responsibility Principle


2. 工厂模式(Factory Pattern

问题

你需要在未指定具体类的情况下创建对象。创建逻辑复杂或依赖于条件。

反面示例

# 客户端代码需要了解所有具体类
class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

# 客户端必须知道实例化哪个类
def get_pet(pet_type):
    if pet_type == "dog":
        return Dog()
    elif pet_type == "cat":
        return Cat()
    # 添加新宠物类型需要修改此函数!

解决方案

from abc import ABC, abstractmethod

# 抽象产品
class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

# 具体产品
class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

class Bird(Animal):
    def speak(self):
        return "Tweet!"

# 工厂
class AnimalFactory:
    @staticmethod
    def create_animal(animal_type):
        animals = {
            'dog': Dog,
            'cat': Cat,
            'bird': Bird
        }

        animal_class = animals.get(animal_type.lower())
        if animal_class:
            return animal_class()
        raise ValueError(f"Unknown animal type: {animal_type}")

# 使用示例
factory = AnimalFactory()
pet = factory.create_animal('dog')
print(pet.speak())  # Woof!

JavaScript 实现

class Animal {
    speak() {
        throw new Error("Method must be implemented");
    }
}

class Dog extends Animal {
    speak() {
        return "Woof!";
    }
}

class Cat extends Animal {
    speak() {
        return "Meow!";
    }
}

class AnimalFactory {
    static createAnimal(animalType) {
        const animals = {
            dog: Dog,
            cat: Cat
        };

        const AnimalClass = animals[animalType.toLowerCase()];
        if (AnimalClass) {
            return new AnimalClass();
        }
        throw new Error(`Unknown animal type: ${animalType}`);
    }
}

// 使用示例
const pet = AnimalFactory.createAnimal('dog');
console.log(pet.speak());  // Woof!

何时使用

  • 适用:当你事先不知道确切类型时,或创建逻辑较为复杂
  • 不适用:用于没有变化的简单对象创建

优缺点

客户端与产品之间的松耦合 易于添加新产品(开闭原则,Open/Closed Principle 集中化的创建逻辑 可能引入大量类


3. 抽象工厂模式(Abstract Factory Pattern

问题

你需要在未指定具体类的情况下创建一组相关的对象家族。

示例:UI 主题工厂

from abc import ABC, abstractmethod

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

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

# 具体产品 —— 浅色主题
class LightButton(Button):
    def render(self):
        return "Rendering light button"

class LightCheckbox(Checkbox):
    def render(self):
        return "Rendering light checkbox"

# 具体产品 —— 深色主题
class DarkButton(Button):
    def render(self):
        return "Rendering dark button"

class DarkCheckbox(Checkbox):
    def render(self):
        return "Rendering dark checkbox"

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

    @abstractmethod
    def create_checkbox(self):
        pass

# 具体工厂
class LightThemeFactory(UIFactory):
    def create_button(self):
        return LightButton()

    def create_checkbox(self):
        return LightCheckbox()

class DarkThemeFactory(UIFactory):
    def create_button(self):
        return DarkButton()

    def create_checkbox(self):
        return DarkCheckbox()

# 客户端代码
def create_ui(factory: UIFactory):
    button = factory.create_button()
    checkbox = factory.create_checkbox()
    return button.render(), checkbox.render()

# 使用示例
light_factory = LightThemeFactory()
print(create_ui(light_factory))

dark_factory = DarkThemeFactory()
print(create_ui(dark_factory))

何时使用

  • 适用:当你需要一组相关的对象协同工作时
  • 不适用:当你只有一个产品家族时

4. 构建器模式(Builder Pattern

问题

你需要逐步构建复杂对象。构造函数参数过多。

反面示例

# 构造函数参数过多
class Pizza:
    def __init__(self, size, cheese=False, pepperoni=False,
                 mushrooms=False, onions=False, bacon=False,
                 ham=False, pineapple=False):
        self.size = size
        self.cheese = cheese
        self.pepperoni = pepperoni
        # ... 大量参数

# 难以阅读,容易出错
pizza = Pizza(12, True, True, False, True, False, True, False)

解决方案

class Pizza:
    def __init__(self, size):
        self.size = size
        self.cheese = False
        self.pepperoni = False
        self.mushrooms = False
        self.onions = False
        self.bacon = False

    def __str__(self):
        toppings = []
        if self.cheese:
            toppings.append("cheese")
        if self.pepperoni:
            toppings.append("pepperoni")
        if self.mushrooms:
            toppings.append("mushrooms")
        if self.onions:
            toppings.append("onions")
        if self.bacon:
            toppings.append("bacon")

        return f"{self.size}\" pizza with {', '.join(toppings)}"

class PizzaBuilder:
    def __init__(self, size):
        self.pizza = Pizza(size)

    def add_cheese(self):
        self.pizza.cheese = True
        return self

    def add_pepperoni(self):
        self.pizza.pepperoni = True
        return self

    def add_mushrooms(self):
        self.pizza.mushrooms = True
        return self

    def add_onions(self):
        self.pizza.onions = True
        return self

    def add_bacon(self):
        self.pizza.bacon = True
        return self

    def build(self):
        return self.pizza

# 使用示例 —— 可读性大大提升!
pizza = (PizzaBuilder(12)
         .add_cheese()
         .add_pepperoni()
         .add_mushrooms()
         .build())

print(pizza)  # 12" pizza with cheese, pepperoni, mushrooms

JavaScript 实现

class Pizza {
    constructor(size) {
        this.size = size;
        this.toppings = [];
    }

    toString() {
        return `${this.size}" pizza with ${this.toppings.join(', ')}`;
    }
}

class PizzaBuilder {
    constructor(size) {
        this.pizza = new Pizza(size);
    }

    addCheese() {
        this.pizza.toppings.push('cheese');
        return this;
    }

    addPepperoni() {
        this.pizza.toppings.push('pepperoni');
        return this;
    }

    addMushrooms() {
        this.pizza.toppings.push('mushrooms');
        return this;
    }

    build() {
        return this.pizza;
    }
}

// 使用示例
const pizza = new PizzaBuilder(12)
    .addCheese()
    .addPepperoni()
    .addMushrooms()
    .build();

console.log(pizza.toString());

何时使用

  • 适用:构造函数参数多、需要逐步构建、需要不可变对象
  • 不适用:参数少的简单对象

优缺点

可读性强的流畅接口(Fluent Interface 对构建过程的精细控制 可以创建不同的表示形式 代码量增加(需要构建器类)


5. 原型模式(Prototype Pattern

问题

你需要复制现有对象,而不让代码依赖于它们的类。

解决方案

import copy

class Prototype:
    def clone(self):
        """对象的深拷贝。"""
        return copy.deepcopy(self)

class Shape(Prototype):
    def __init__(self, shape_type, color):
        self.shape_type = shape_type
        self.color = color
        self.coordinates = []

    def __str__(self):
        return f"{self.color} {self.shape_type} at {self.coordinates}"

# 使用示例
original = Shape("Circle", "Red")
original.coordinates = [10, 20]

# 克隆
clone = original.clone()
clone.color = "Blue"
clone.coordinates = [30, 40]

print(original)  # Red Circle at [10, 20]
print(clone)     # Blue Circle at [30, 40]

JavaScript 实现

class Shape {
    constructor(shapeType, color) {
        this.shapeType = shapeType;
        this.color = color;
        this.coordinates = [];
    }

    clone() {
        const cloned = Object.create(Object.getPrototypeOf(this));
        cloned.shapeType = this.shapeType;
        cloned.color = this.color;
        cloned.coordinates = [...this.coordinates];
        return cloned;
    }

    toString() {
        return `${this.color} ${this.shapeType} at ${this.coordinates}`;
    }
}

// 使用示例
const original = new Shape("Circle", "Red");
original.coordinates = [10, 20];

const clone = original.clone();
clone.color = "Blue";
clone.coordinates = [30, 40];

console.log(original.toString());  // Red Circle at 10,20
console.log(clone.toString());     // Blue Circle at 30,40

何时使用

  • 适用:对象创建成本高,需要大量相似对象
  • 不适用:简单对象,浅拷贝即可满足需求

模式选择指南

模式 适用场景 典型用例
单例模式(Singleton 需要唯一实例 日志记录器、配置管理、数据库连接池
工厂模式(Factory 编译时不知道具体类 插件系统、文档类型
抽象工厂模式(Abstract Factory 需要一组相关的对象 UI 主题、跨平台应用
构建器模式(Builder 参数众多的复杂构建过程 查询构建器、文档构建器
原型模式(Prototype 创建成本高,需要副本 游戏实体、图形编辑器

应避免的反模式

1. 过度使用单例

# 不要把所有东西都做成单例
class MathUtils(Singleton):  # 糟糕 —— 直接使用模块即可!
    @staticmethod
    def add(a, b):
        return a + b

# 应使用模块级函数
def add(a, b):
    return a + b

2. 上帝工厂

# 不要用一个工厂处理所有事情
class GodFactory:
    def create_user(self): ...
    def create_product(self): ...
    def create_order(self): ...
    # ... 还有 50 多个方法

# 应按不同关注点使用独立的工厂
class UserFactory: ...
class ProductFactory: ...
class OrderFactory: ...

3. 过早抽象

# 不要在简单情况下创建工厂
class DogFactory:
    @staticmethod
    def create():
        return Dog()  # 只有一个简单的类

# 应直接实例化
dog = Dog()

关键要点

  1. 单例模式:唯一实例,全局访问
  2. 工厂模式:将对象创建与使用解耦
  3. 抽象工厂模式:一组相关的对象家族
  4. 构建器模式:逐步构建复杂对象
  5. 原型模式:克隆现有对象

请记住:在模式确实能解决实际问题时再使用。不要在模式不适用时强行套用!