抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建一系列相关或依赖的对象族,而无需指定它们的具体类。通俗地讲,你可以将其理解为一种“产品簇”的生成器。例如,针对不同地区(如美国、法国),同一组输入(如日期和金额)需要生成不同格式的输出。
这种模式的优势在于,当你需要新增一套输出规则时(例如支持中国格式),只需增加一个新的具体工厂类或模块,并在工厂导向器中注册即可,无需修改已有的客户端代码,这符合开闭原则,极大地提升了系统的可扩展性。
下面,我们通过一个国际化场景下的日期与货币格式化实例,来演示一种非常Pythonic的实现方式,它利用了模块和函数作为基本的构建单元。
项目结构
首先,我们创建以下目录和文件结构:
abstract_factory/
├── __init__.py
├── format_factory.py # 客户端调用入口
└── backends/ # 具体工厂实现目录
├── USA.py
└── France.py
步骤1:实现具体工厂(产品簇)
每个具体工厂模块负责实现一整套格式化规则。
backends/USA.py (美国格式工厂)
def format_date(year: int, month: int, day: int) -> str:
return f"{month}/{day}/{year}"
def format_currency(amount: int, currency: int) -> str:
"""
格式化货币。
:param amount: 金额的整数部分
:param currency: 金额的小数部分
:return: 格式化后的货币字符串(如 $12,155,888.85)
"""
# 将整数部分按千位添加逗号分隔符
amount_str = str(amount)
formatted_int = ''.join(reversed([
(',' if n+1 != len(amount_str) else '') + m if not (n+1) % 3 else m
for n, m in enumerate(reversed(amount_str))
]))
# 确保小数部分为两位
formatted_dec = f"{currency:02d}"
return f"${formatted_int}.{formatted_dec}"
backends/France.py (法国/欧洲格式工厂)
def format_date(year: int, month: int, day: int) -> str:
return f"{day}-{month}-{year}"
def format_currency(amount: int, currency: int) -> str:
amount_str = str(amount)
formatted_int = ''.join(reversed([
(',' if n+1 != len(amount_str) else '') + m if not (n+1) % 3 else m
for n, m in enumerate(reversed(amount_str))
]))
formatted_dec = f"{currency:02d}"
return f"€{formatted_int}.{formatted_dec}"
步骤2:实现抽象工厂(产品导向器)
在 abstract_factory/init.py 中,我们创建一个简单的映射(字典)作为工厂导向器。这是整个抽象工厂模式的核心,它根据传入的标识符(如国家代码)返回对应的具体工厂模块。
from .backends import USA, France
def format_native(country_code: str):
"""工厂导向器:根据国家代码返回对应的格式化模块(产品簇)。"""
factory = {
'US': USA, # 映射到美国格式模块
'FR': France, # 映射到法国格式模块
# 未来扩展:在此添加新的国家代码与模块的映射,如 'CN': China
}
return factory.get(country_code.upper()) # 返回具体的工厂模块
步骤3:客户端调用
最后,在 format_factory.py 中,我们创建客户端类来使用这个抽象工厂。客户端代码与具体的工厂实现解耦,只依赖于抽象的format_native函数。
import abstract_factory
class FormatFactory:
"""客户端使用的格式化工厂门面。"""
def __init__(self, country_code: str) -> None:
self.country_code = country_code
# 通过导向器获取具体工厂模块
self._factory_module = abstract_factory.format_native(self.country_code)
def get_date_format(self, year: int, month: int, day: int) -> str:
"""获取格式化后的日期字符串。"""
return self._factory_module.format_date(year, month, day)
def get_currency_format(self, base: int, currency: int) -> str:
"""获取格式化后的货币字符串。"""
return self._factory_module.format_currency(base, currency)
# 使用示例
if __name__ == '__main__':
# 创建美国格式工厂产品
product_us = FormatFactory('US')
# 创建法国格式工厂产品
product_fr = FormatFactory('FR')
print(product_fr.get_currency_format(12155888, 85))
# 输出: €12,155,888.85
print(product_us.get_currency_format(12155888, 85))
# 输出: $12,155,888.85
print(product_fr.get_date_format(2025, 12, 23))
# 输出: 23-12-2025
print(product_us.get_date_format(2025, 12, 23))
# 输出: 12/23/2025
运行结果
成功运行后,终端将输出符合美、法两国格式规范的日期和货币字符串,清晰地展示了同一组数据在不同“工厂”下的差异化表现。
模式总结
通过上述实现,我们完成了一个清晰、Pythonic的抽象工厂模式。其核心思想是利用模块组织代码,并通过一个中心化的导向器(字典映射)来动态选择不同的“产品簇”(格式化规则集)。当业务需要支持新的地区(如中国)时,开发者只需:
- 在
backends/目录下创建新的模块(如China.py),实现相同的接口函数。
- 在
abstract_factory/__init__.py的导向器字典中添加一行映射(如'CN': China)。
整个扩展过程对现有的客户端代码和已实现的其他工厂模块完全无侵入,体现了良好的设计模式实践。