工厂模式三种形态全解析简单工厂 · 工厂方法 · 抽象工厂——何时用哪个一、引言为什么创建对象也需要仪式感如果你曾经写过这样的代码——ifanimal_typedog:returnDog()elifanimal_typecat:returnCat()elifanimal_typebird:returnBird()# ... 后续还要加鱼、兔子、仓鼠...那么恭喜你你已经触碰到了设计模式中最古老也最实用的议题对象创建的混乱问题。随着业务扩展这段代码将变成一个脆弱的「上帝函数」——每次新增类型都要打开它修改每次修改都有引入新 Bug 的风险。工厂模式Factory Pattern就是解决这类问题的利器。它将「创建对象的逻辑」从「使用对象的代码」中分离出来让代码具备更好的扩展性、可维护性和可测试性。工厂模式并非一种单一模式而是一个家族包含三种形态简单工厂Simple Factory——最直接的封装工厂方法Factory Method——面向接口的扩展抽象工厂Abstract Factory——产品族级别的解耦本文将通过完整的 Python 代码示例带你彻底弄清楚这三者的区别以及在实际项目中应该如何选择。二、简单工厂最朴素的封装思维2.1 核心思想简单工厂并不是 GoF 23 种设计模式中的一员但它是理解工厂系列模式的起点。其核心思想极为朴素将对象的创建逻辑集中到一个「工厂类」或「工厂函数」中客户端只需告诉工厂「我要什么」不必关心「怎么造」。2.2 Python 实现假设我们在开发一款日志系统需要支持控制台日志、文件日志和数据库日志三种输出方式fromabcimportABC,abstractmethod# 抽象产品classLogger(ABC):abstractmethoddeflog(self,message:str)-None:pass# 具体产品classConsoleLogger(Logger):deflog(self,message:str)-None:print(f[CONSOLE]{message})classFileLogger(Logger):def__init__(self,filepath:str):self.filepathfilepathdeflog(self,message:str)-None:withopen(self.filepath,a)asf:f.write(f[FILE]{message}\n)classDatabaseLogger(Logger):deflog(self,message:str)-None:print(f[DB] INSERT INTO logs VALUES ({message}))# 简单工厂classLoggerFactory:staticmethoddefcreate_logger(logger_type:str,**kwargs)-Logger:loggers{console:ConsoleLogger,file:FileLogger,database:DatabaseLogger,}clsloggers.get(logger_type.lower())ifclsisNone:raiseValueError(f未知的日志类型:{logger_type})returncls(**kwargs)# 客户端代码loggerLoggerFactory.create_logger(console)logger.log(系统启动成功)file_loggerLoggerFactory.create_logger(file,filepathapp.log)file_logger.log(用户登录)2.3 优缺点分析优点客户端与具体产品类完全解耦创建逻辑集中修改方便实现简单学习成本低。缺点工厂类承担了过多职责每新增一种产品就必须修改工厂类违反了「开闭原则」对扩展开放对修改关闭当产品类型激增时工厂类会变得臃肿难以维护。适用场景简单工厂适合产品类型稳定、数量较少3~5 种的场景比如配置驱动的策略选择、简单插件加载等。三、工厂方法让扩展「不动旧代码」3.1 核心思想工厂方法模式是 GoF 中正式的设计模式。它将「创建哪种对象」的决策权下放到子类每个具体工厂类只负责创建一种产品。这样新增产品时只需新增工厂类不需要修改已有代码完美遵循开闭原则。UML 结构要点抽象工厂Creator定义工厂方法接口具体工厂ConcreteCreator实现工厂方法抽象产品Product定义产品接口具体产品ConcreteProduct实现产品接口3.2 Python 实现继续我们的日志系统将其升级为工厂方法模式fromabcimportABC,abstractmethod# 抽象工厂classLoggerCreator(ABC):abstractmethoddefcreate_logger(self)-Logger:工厂方法子类必须实现passdefget_logger_and_log(self,message:str)-None:模板方法使用工厂方法创建的产品loggerself.create_logger()logger.log(message)# 具体工厂classConsoleLoggerCreator(LoggerCreator):defcreate_logger(self)-Logger:returnConsoleLogger()classFileLoggerCreator(LoggerCreator):def__init__(self,filepath:str):self.filepathfilepathdefcreate_logger(self)-Logger:returnFileLogger(self.filepath)# 新增产品——无需修改任何已有代码classSlackLogger(Logger):deflog(self,message:str)-None:print(f[SLACK] 发送消息到频道:{message})classSlackLoggerCreator(LoggerCreator):defcreate_logger(self)-Logger:returnSlackLogger()# 客户端代码依赖抽象不依赖具体defrun_with_logger(creator:LoggerCreator,msg:str):creator.get_logger_and_log(msg)run_with_logger(ConsoleLoggerCreator(),服务器启动)run_with_logger(FileLoggerCreator(sys.log),数据库连接成功)run_with_logger(SlackLoggerCreator(),⚠️ 异常警报)3.3 结合配置文件实现动态加载工厂方法模式可以结合 Python 的反射机制实现配置驱动的动态工厂importimportlib LOGGER_CONFIG{type:SlackLogger,# 来自配置文件module:__main__}defload_logger_from_config(config:dict)-Logger:moduleimportlib.import_module(config[module])clsgetattr(module,config[type])returncls()loggerload_logger_from_config(LOGGER_CONFIG)logger.log(配置驱动日志系统启动)3.4 优缺点分析优点严格遵循开闭原则新增产品零改动工厂与产品一一对应单一职责客户端只依赖抽象代码高度解耦。缺点每种产品对应一个工厂类类数量随产品增长而线性增加当产品之间存在组合关系产品族时工厂方法力不从心。适用场景工厂方法适合产品种类多、需要频繁扩展的场景如插件化系统、多渠道通知、多数据库适配器等。四、抽象工厂驾驭产品族的高维战场4.1 核心思想当系统中存在多个「产品族」且同一族内的产品必须协同使用时抽象工厂模式闪亮登场。它提供一个接口用于创建一系列相关或相互依赖的对象而无需指定它们的具体类。经典场景你要开发一套跨平台 UI 组件库Windows 风格和 macOS 风格各有一套按钮Button、文本框TextInput、复选框Checkbox。同一界面中的组件必须来自同一风格——这就是「产品族」。4.2 Python 实现fromabcimportABC,abstractmethod# 抽象产品层 classButton(ABC):abstractmethoddefrender(self)-str:passabstractmethoddefon_click(self)-str:passclassTextInput(ABC):abstractmethoddefrender(self)-str:passabstractmethoddefget_value(self)-str:passclassCheckbox(ABC):abstractmethoddefrender(self)-str:pass# Windows 产品族 classWindowsButton(Button):defrender(self)-str:returnWinButton styleflat/defon_click(self)-str:returnWindows click eventclassWindowsTextInput(TextInput):def__init__(self):self._valuedefrender(self)-str:returnWinInput/defget_value(self)-str:returnself._valueclassWindowsCheckbox(Checkbox):defrender(self)-str:returnWinCheckbox/# macOS 产品族 classMacButton(Button):defrender(self)-str:returnMacButton stylerounded/defon_click(self)-str:returnmacOS click eventclassMacTextInput(TextInput):def__init__(self):self._valuedefrender(self)-str:returnMacInput/defget_value(self)-str:returnself._valueclassMacCheckbox(Checkbox):defrender(self)-str:returnMacCheckbox/# 抽象工厂 classUIFactory(ABC):abstractmethoddefcreate_button(self)-Button:passabstractmethoddefcreate_text_input(self)-TextInput:passabstractmethoddefcreate_checkbox(self)-Checkbox:pass# 具体工厂 classWindowsUIFactory(UIFactory):defcreate_button(self)-Button:returnWindowsButton()defcreate_text_input(self)-TextInput:returnWindowsTextInput()defcreate_checkbox(self)-Checkbox:returnWindowsCheckbox()classMacUIFactory(UIFactory):defcreate_button(self)-Button:returnMacButton()defcreate_text_input(self)-TextInput:returnMacTextInput()defcreate_checkbox(self)-Checkbox:returnMacCheckbox()# 客户端 classLoginDialog:def__init__(self,factory:UIFactory):self.buttonfactory.create_button()self.usernamefactory.create_text_input()self.remember_mefactory.create_checkbox()defrender(self):print(登录界面渲染:)print(f 用户名输入框:{self.username.render()})print(f 记住密码:{self.remember_me.render()})print(f 登录按钮:{self.button.render()})# 根据操作系统选择工厂importplatform factoryMacUIFactory()ifplatform.system()DarwinelseWindowsUIFactory()dialogLoginDialog(factory)dialog.render()4.3 实战变体结合注册表实现可扩展工厂fromtypingimportType# 工厂注册表_factory_registry:dict[str,Type[UIFactory]]{windows:WindowsUIFactory,macos:MacUIFactory,}defget_ui_factory(platform:str)-UIFactory:factory_cls_factory_registry.get(platform.lower())ifnotfactory_cls:raiseValueError(f不支持的平台:{platform})returnfactory_cls()# 扩展新平台如Linux只需注册不动已有代码classLinuxUIFactory(UIFactory):defcreate_button(self)-Button:returnWindowsButton()defcreate_text_input(self)-TextInput:returnWindowsTextInput()defcreate_checkbox(self)-Checkbox:returnWindowsCheckbox()_factory_registry[linux]LinuxUIFactory4.4 优缺点分析优点保证同族产品一致性不会出现「Windows按钮配Mac文本框」的混用问题切换整个产品族只需替换工厂产品族之间高度隔离便于测试。缺点架构复杂度较高类的数量随产品族线性增长若需要新增一种产品如新增 Dropdown必须修改所有具体工厂类扩展不够灵活。⚠️注意抽象工厂的最大挑战在于产品接口变化时所有具体工厂都需修改。因此在产品接口未稳定前不要急于引入抽象工厂。五、三种形态横向对比模式优势劣势适用场景简单工厂中心化创建易维护违反开闭原则不易扩展产品类型少且稳定工厂方法符合开闭原则扩展灵活类数量随产品增多产品类型多需独立扩展抽象工厂产品族一致性强解耦彻底架构复杂学习曲线陡多产品族需保证兼容性5.1 决策树如何快速选择产品类型少≤5且变化不频繁 →简单工厂产品类型多、变化频繁但每次只需一种产品 →工厂方法产品有「家族」概念需要保证族内一致性 →抽象工厂不确定未来是否需要扩展 →先简单工厂遇瓶颈再重构 「过早的复杂化是万恶之源。」如果项目规模较小简单工厂往往是最优解。遇到实际痛点时再升级比一开始就用抽象工厂要理智得多。六、最佳实践与常见陷阱6.1 利用 Python 特性简化工厂Python 作为动态语言可以用字典映射极大简化简单工厂告别冗长的 if-elif 链PRODUCT_MAP{a:ProductA,b:ProductB,c:ProductC,}deffactory(product_type:str,**kwargs):clsPRODUCT_MAP.get(product_type)ifclsisNone:raiseValueError(fUnknown:{product_type})returncls(**kwargs)6.2 使用 TypeVar 增强类型安全fromtypingimportTypeVar,Type,Generic TTypeVar(T,boundBaseProduct)classBaseFactory(Generic[T]):_registry:dict[str,Type[T]]{}classmethoddefregister(cls,name:str,product_cls:Type[T]):cls._registry[name]product_clsclassmethoddefcreate(cls,name:str,**kwargs)-T:product_clscls._registry.get(name)ifnotproduct_cls:raiseKeyError(f{name}not registered)returnproduct_cls(**kwargs)6.3 工厂模式与单例模式结合工厂本身通常应设计为单例避免重复创建工厂实例带来的开销classSingletonMeta(type):_instances{}def__call__(cls,*args,**kwargs):ifclsnotincls._instances:cls._instances[cls]super().__call__(*args,**kwargs)returncls._instances[cls]classAppLoggerFactory(LoggerCreator,metaclassSingletonMeta):defcreate_logger(self)-Logger:returnConsoleLogger()# 可根据配置动态切换6.4 单元测试中的妙用工厂模式最大的测试价值在测试时注入 Mock 工厂完全隔离外部依赖classMockLogger(Logger):def__init__(self):self.messages[]deflog(self,message:str)-None:self.messages.append(message)classMockLoggerCreator(LoggerCreator):def__init__(self):self.loggerMockLogger()defcreate_logger(self):returnself.loggerdeftest_business_logic_logs_correctly():factoryMockLoggerCreator()run_with_logger(factory,测试消息)assert测试消息infactory.logger.messages七、总结工厂模式家族是面向对象设计中「创建型模式」的核心。三种形态并不是竞争关系而是解决问题的三个不同维度简单工厂解决「创建逻辑散落各处」的混乱问题是第一步封装。工厂方法解决「增加新产品需要修改已有代码」的扩展性问题是面向对象的优雅实践。抽象工厂解决「多产品族一致性」的协同问题是大型系统架构的利器。在实际项目中没有「最好的模式」只有「最合适的模式」。一个好的架构师会根据产品数量、变化频率、团队规模灵活选择而不是机械地套用最复杂的方案。 设计模式的本质是「经验的结晶」而非「规范的枷锁」。理解背后的动机与权衡远比记住实现细节更重要。如果这篇文章对你有帮助欢迎在评论区分享你在项目中是如何选择工厂模式的遇到了哪些有趣的设计取舍让我们一起构建更好的代码。参考资料《Design Patterns: Elements of Reusable Object-Oriented Software》- GoF《流畅的Python》- Luciano RamalhoPython 官方文档 ABC 模块https://docs.python.org/3/library/abc.htmlPEP 8 编码规范https://peps.python.org/pep-0008/Refactoring Guru 工厂模式https://refactoring.guru/design-patterns/factory-method/python/example