0%

工厂函数与工厂方法

本文以python为例,简单介绍工厂函数。

工厂函数

工厂函数(Factory Function)是一种常见的设计模式,尤其适用于面向对象编程和函数式编程场景中,用来 动态创建对象封装复杂初始化逻辑

Definition

工厂函数本质上是一个返回对象的函数,而不是类的构造函数(init)。它根据传入的参数决定返回什么类型或配置的对象。

1
2
3
4
5
6
7
def animal_factory(animal_type):
if animal_type == "dog":
return Dog()
elif animal_type == "cat":
return Cat()
else:
raise ValueError("Unknown animal type")

工厂函数的优势在于:

  1. 封装复杂逻辑:隐藏创建对象所需的复杂初始化步骤;
  2. 运行时类型决定:可以在运行时根据参数决定返回何种子类;
  3. 返回同一类的多个变种:支持不同配置、不同状态的实例;
  4. 替代类继承:在某些函数式编程场景中,不用类继承,也可以通过工厂函数生成封装好的行为;
  5. 支持缓存或单例:结合缓存机制(比如字典)可复用实例。

Usage

数据库连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MySQLConnection:
def connect(self):
return "MySQL connected"

class PostgreSQLConnection:
def connect(self):
return "PostgreSQL connected"

def db_connection_factory(db_type):
if db_type == "mysql":
return MySQLConnection()
elif db_type == "postgres":
return PostgreSQLConnection()
else:
raise ValueError("Unsupported DB type")

conn = db_connection_factory("mysql")
print(conn.connect()) # 输出: MySQL connected

工厂函数封装初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
from datetime import datetime

def create_logger(log_type="file"):
if log_type == "file":
return lambda msg: print(f"[FILE] {datetime.now()}: {msg}")
elif log_type == "console":
return lambda msg: print(f"[CONSOLE] {datetime.now()}: {msg}")
else:
raise ValueError("Unknown log type")

logger = create_logger("console")
logger("Something happened") # 输出: [CONSOLE] 时间: Something happened

结合缓存,返回单例或共享对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
_instances = {}

def singleton_factory(cls):
def get_instance(*args, **kwargs):
if cls not in _instances:
_instances[cls] = cls(*args, **kwargs)
return _instances[cls]
return get_instance

@singleton_factory
class Configuration:
def __init__(self):
self.settings = {}

config1 = Configuration()
config2 = Configuration()

assert config1 is config2

在类中使用工厂函数

现在有一需求,我们想为每个宠物自动分配一个出生时间,给它一个独立的玩具箱(字典),这些值在每次创建时应该不同。
✅ 正确做法:使用default_factory

1
2
3
4
5
6
7
8
9
10
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict

@dataclass
class Pet:
name: str
kind: str # "cat" or "dog"
born_time: datetime = field(default_factory=datetime.now)
toys: Dict[str, int] = field(default_factory=dict) # 玩具名 -> 数量

field函数: 使用 field(default_factory=datetime.now) 表示:每次创建 Pet 实例时,都调用 datetime.now() 来自动生成时间戳(不是在类加载时就固定)。

field 是 Python dataclasses 模块中的一个函数,用来给类的属性定义更复杂的行为或默认值。default_factory 是 field() 函数的一个参数,它的作用是指定一个“工厂函数”,每次创建 dataclass 实例时调用它来生成字段的默认值。

1
2
3
4
5
6
7
8
9
10
cat = Pet(name="Mimi", kind="cat")
dog = Pet(name="Doudou", kind="dog")

cat.toys["ball"] = 2

print(cat.born_time) # 每个宠物自己的出生时间
print(dog.born_time) # 不同时间
print(dog.toys) # {}

print(cat.toys is dog.toys) # False ✅,玩具箱不共享

❌ 错误示范:不使用default_factory,所有宠物共享玩具箱和出生时间

1
2
3
4
5
6
@dataclass
class BrokenPet:
name: str
kind: str
born_time: datetime = datetime.now() # ❌ 类加载时固定
toys: Dict[str, int] = {} # ❌ 所有宠物共享一套玩具
1
2
3
4
5
6
7
8
cat = BrokenPet(name="Mimi", kind="cat")
dog = BrokenPet(name="Doudou", kind="dog")

cat.toys["fish"] = 1

print(dog.toys) # ❌ {'fish': 1},狗和猫共用玩具箱

print(cat.born_time == dog.born_time) # True ❌,出生时间完全相同

有时人们会混淆类属性实例属性,导致共享值错误!!!!

工厂方法

厂方法模式(Factory Method Pattern本质是定义一个接口(抽象方法),让子类决定要实例化的类:

  • 是 GoF 的正式设计模式之一;
  • 基于类继承;
  • 通常用在面向对象的架构中;
  • Python中可以用 abc(抽象基类)来实现。

一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 AnimalFactory(ABC):
@abstractmethod
def create_animal(self):
pass

class DogFactory(AnimalFactory):
def create_animal(self):
return Dog()

class CatFactory(AnimalFactory):
def create_animal(self):
return Cat()

factory = DogFactory()
animal = factory.create_animal()
print(animal.speak()) # 输出:Woof

DogFactory 和 CatFactory 继承了 AnimalFactory,并各自实现了创建逻辑。