如果说昨天的工厂方法是针对单个产品(比如只生产卡车),那么今天的抽象工厂就是针对产品族(比如生产一整套豪华汽车组件,包括豪华车轮、豪华方向盘)。
Day 3:抽象工厂模式 (Abstract Factory Pattern)
1. 它是怎么来的?
想象一下,你正在开发一款跨平台的 UI 工具包(支持 Windows 和 Mac)。
UI 组件有很多种:按钮(Button)、复选框(Checkbox)、文本框(TextField)。
如果你用昨天的工厂方法,你得写一个 ButtonFactory、一个 CheckboxFactory。
但这里有一个致命的隐患:你必须确保 Windows 系统上创建的按钮和复选框都是 Windows 风格的,绝对不能在 Mac 系统里误用了一个 Windows 风格的按钮。
这就引入了“产品族”的概念。Windows 的按钮和 Windows 的复选框属于同一个产品族;Mac 的按钮和 Mac 的复选框属于另一个产品族。
抽象工厂模式就是为了解决“一系列相互依赖的产品的创建问题”而诞生的:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
2. 适用场景
- 产品族群:系统需要由多个相关联的产品对象组成,且它们必须配套使用。
- 跨平台/多主题切换:如支持多种操作系统界面(Windows/macOS/Linux)、更换软件皮肤主题(暗黑模式/明亮模式)。
- 解耦依赖:客户端不应该依赖具体产品的创建细节,只通过高度抽象的接口与产品族交互。
3. 优缺点分析
| 优点 |
缺点 |
| 保证产品族一致性:强制同一个工厂生产出来的产品都在一起配套使用,绝不会出现“混搭”Bug。 |
扩展“新产品”极难(违反开闭原则):如果产品族里要新增一个组件(比如加一个下拉框 Dropdown),你需要修改抽象工厂以及所有的具体工厂类。 |
| 高解耦:客户端代码与具体平台、具体产品的实现完全隔离。 |
代码结构复杂:抽象层级非常深,类的数量会成倍增加。 |
| 符合单一职责原则:将特定产品族的创建代码集中到一个地方。 |
|
4. Python 代码实现
💡 简单实例:跨平台 UI 组件库(Windows vs Mac)
我们来用代码解决上面提到的 UI 组件配套问题。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| from abc import ABC, abstractmethod
class Button(ABC): @abstractmethod def paint(self) -> str: pass
class Checkbox(ABC): @abstractmethod def render(self) -> str: pass
class WinButton(Button): def paint(self): return "渲染 [Windows 风格按钮]"
class WinCheckbox(Checkbox): def render(self): return "渲染 [Windows 风格复选框]"
class MacButton(Button): def paint(self): return "渲染 [Mac 风格按钮]"
class MacCheckbox(Checkbox): def render(self): return "渲染 [Mac 风格复选框]"
class GUIFactory(ABC): """抽象工厂定义了创建整个产品族(按钮和复选框)的接口""" @abstractmethod def create_button(self) -> Button: pass
@abstractmethod def create_checkbox(self) -> Checkbox: pass
class WinFactory(GUIFactory): """Windows 工厂只生产 Windows 组件""" def create_button(self): return WinButton() def create_checkbox(self): return WinCheckbox()
class MacFactory(GUIFactory): """Mac 工厂只生产 Mac 组件""" def create_button(self): return MacButton() def create_checkbox(self): return MacCheckbox()
def render_ui(factory: GUIFactory): button = factory.create_button() checkbox = factory.create_checkbox() print(button.paint()) print(checkbox.render())
print("--- 初始化 Windows UI ---") render_ui(WinFactory())
print("\n--- 初始化 Mac UI ---") render_ui(MacFactory())
|
🚀 复杂实例:多数据库架构适配器(MySQL vs MongoDB)
在现代后端架构中,抽象工厂常用于多数据库/多存储源的切换。
假设你的应用需要同时支持关系型数据库(MySQL)和 NoSQL 数据库(MongoDB),它们各自都有一套独立的“连接器”和“查询器”。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| from abc import ABC, abstractmethod
class DBConnection(ABC): @abstractmethod def connect(self): pass
class DBCommand(ABC): @abstractmethod def execute_query(self, query_str: str): pass
class MySQLConnection(DBConnection): def connect(self): return "MySQL: 已建立数据库连接池。"
class MySQLCommand(DBCommand): def execute_query(self, query_str): return f"MySQL 执行 SQL: SELECT * FROM {query_str}"
class MongoDBConnection(DBConnection): def connect(self): return "MongoDB: 已连接到集群副本集。"
class MongoDBCommand(DBCommand): def execute_query(self, query_str): return f"MongoDB 执行 BSON 查询: db.collection.find({{ {query_str} }})"
class DatabaseFactory(ABC): @abstractmethod def get_connection(self) -> DBConnection: pass @abstractmethod def get_command(self) -> DBCommand: pass
class RelationalDBFactory(DatabaseFactory): def get_connection(self): return MySQLConnection() def get_command(self): return MySQLCommand()
class NoSQLDBFactory(DatabaseFactory): def get_connection(self): return MongoDBConnection() def get_command(self): return MongoDBCommand()
class Application: def __init__(self, factory: DatabaseFactory): self.factory = factory self.connection = self.factory.get_connection() self.command = self.factory.get_command()
def run_business_logic(self, target: str): print(self.connection.connect()) print(self.command.execute_query(target))
if __name__ == "__main__": current_env = "nosql" if current_env == "relational": factory = RelationalDBFactory() else: factory = NoSQLDBFactory() app = Application(factory) app.run_business_logic("users")
|
区分总结:三大工厂到底有什么区别?
- 简单工厂(不是设计模式):一个工厂类负责根据参数创建所有产品。新增产品时要改动工厂内的 if-else。
- 工厂方法模式:一个工厂只对应一种产品(卡车工厂造卡车,轮船工厂造轮船)。
- 抽象工厂模式:一个工厂负责生产一整套关联的产品族(Windows工厂既造Win按钮,又造Win复选框)。