日常写代码的时候,总会遇到对象创建的问题。有时候对象创建过程很复杂,有时候需要灵活控制对象的类型,有时候又希望全局只有一个实例。这时候就需要创建型设计模式来帮忙了。
创建型模式一共有五个,咱们一个一个来看。
单例模式
场景:数据库连接池、配置管理器这类全局唯一的对象。
单例模式确保一个类只有一个实例,并提供全局访问点。
classDatabaseConnection: _instance = None _connection = Nonedef __new__(cls):if cls._instance is None: cls._instance = super().__new__(cls)return cls._instancedef connect(self):if not self._connection:print("创建新的数据库连接")self._connection = "connected"else:print("使用已存在的连接")return self._connection# 使用db1 = DatabaseConnection()db2 = DatabaseConnection()print(db1 is db2) # Truedb1.connect() # 创建新的数据库连接db2.connect() # 使用已存在的连接
Python 的 __new__ 方法在这里很关键,它在 __init__ 之前执行,适合控制实例的创建。
不过单例模式也有坑。测试的时候可能会遇到状态污染,多线程环境下要注意线程安全。
工厂方法模式
场景:日志框架(支持多种日志输出方式)、文件解析器(不同文件类型用不同解析器)。
工厂方法把对象的创建延迟到子类,让子类决定实例化哪个类。
from abc import ABC, abstractmethodclass Parser(ABC): @abstractmethoddef parse(self, content):passclass JSONParser(Parser):def parse(self, content):print(f"解析 JSON: {content}")return {"data": "json_result"}class XMLParser(Parser):def parse(self, content):print(f"解析 XML: {content}")return {"data": "xml_result"}class ParserFactory(ABC): @abstractmethoddef create_parser(self):passclass JSONParserFactory(ParserFactory):def create_parser(self):return JSONParser()class XMLParserFactory(ParserFactory):def create_parser(self):return XMLParser()# 使用json_factory = JSONParserFactory()json_parser = json_factory.create_parser()json_parser.parse('{"key": "value"}')xml_factory = XMLParserFactory()xml_parser = xml_factory.create_parser()xml_parser.parse('<root>value</root>')
工厂方法的好处是新增产品类型时不用改现有代码,只需加新的工厂类就行。但类会变多,这是代价。
抽象工厂模式
场景:跨平台 UI 组件(Windows 风格按钮 vs Mac 风格按钮)、不同品牌的数据库驱动族。
抽象工厂创建一组相关或相互依赖的对象,而不需要指定具体类。
from abc import ABC, abstractmethodclass Button(ABC): @abstractmethoddef render(self):passclass Window(ABC): @abstractmethoddef render(self):passclass WindowsButton(Button):def render(self):print("渲染 Windows 风格按钮")class WindowsWindow(Window):def render(self):print("渲染 Windows 风格窗口")class MacButton(Button):def render(self):print("渲染 Mac 风格按钮")class MacWindow(Window):def render(self):print("渲染 Mac 风格窗口")class GUIFactory(ABC): @abstractmethoddef create_button(self):pass @abstractmethoddef create_window(self):passclass WindowsFactory(GUIFactory):def create_button(self):return WindowsButton()def create_window(self):return WindowsWindow()class MacFactory(GUIFactory):def create_button(self):return MacButton()def create_window(self):return MacWindow()# 使用def create_app(factory: GUIFactory): button = factory.create_button() window = factory.create_window() button.render() window.render()# Windows 风格create_app(WindowsFactory())# Mac 风格create_app(MacFactory())
抽象工厂适合需要保证产品族一致性的场景。但如果要新增一种产品(比如加个 Menu),就要改所有工厂类,扩展性一般。
建造者模式
场景:SQL 查询构建器、复杂 HTML 页面构建、配置对象组装。
建造者模式把复杂对象的构建过程分离出来,同样的构建过程可以创建不同的表示。
classSQLQuery:def__init__(self):self._select = []self._from = Noneself._where = []self._limit = Nonedef add_select(self, columns):self._select.extend(columns)def set_from(self, table):self._from = tabledef add_where(self, condition):self._where.append(condition)def set_limit(self, limit):self._limit = limitdef build(self): query = "SELECT " + ", ".join(self._select) query += f" FROM {self._from}"if self._where: query += " WHERE " + " AND ".join(self._where)if self._limit: query += f" LIMIT {self._limit}"return queryclass QueryBuilder:def __init__(self):self._query = SQLQuery()def select(self, *columns):self._query.add_select(columns)return selfdef from_table(self, table):self._query.set_from(table)return selfdef where(self, condition):self._query.add_where(condition)return selfdef limit(self, limit):self._query.set_limit(limit)return selfdef build(self):return self._query.build()# 使用query = (QueryBuilder() .select("id", "name", "email") .from_table("users") .where("age > 18") .where("status = 'active'") .limit(10) .build())print(query)# 输出: SELECT id, name, email FROM users WHERE age > 18 AND status = 'active' LIMIT 10
链式调用让代码读起来很顺口,构建过程也清晰。但如果对象结构很简单,用建造者模式就有点过度设计了。
原型模式
场景:需要频繁创建相似对象且创建成本高(比如复杂的图形对象)、对象初始化依赖外部资源。
原型模式通过复制现有对象来创建新对象,而不是通过 new 操作。
import copyclass Document:def __init__(self, content, formatting=None):self.content = contentself.formatting = formatting or {"font": "Arial", "size": 12}def clone(self):return copy.deepcopy(self)def __str__(self):return f"Content: {self.content}, Formatting: {self.formatting}"# 使用template = Document("这是文档模板", {"font": "Times New Roman", "size": 14, "bold": True})# 复制并修改doc1 = template.clone()doc1.content = "文档 1 的内容"doc1.formatting["size"] = 16doc2 = template.clone()doc2.content = "文档 2 的内容"doc2.formatting["bold"] = Falseprint(template)print(doc1)print(doc2)# 深拷贝确保修改不影响原型assert template.formatting["size"] == 14assert doc1.formatting["size"] == 16assert doc2.formatting["bold"] == False
Python 的 copy 模块提供了 copy() 浅拷贝和 deepcopy() 深拷贝。原型模式在对象创建开销大或者需要动态调整对象结构时很实用。
总结
创建型模式的共同点是把对象创建和使用分离,让代码更灵活、更易扩展。
设计模式不是银弹,用对了是利器,用错了是负担。实际项目中要根据场景权衡,不要为了用模式而用模式。