简单工厂 · 工厂方法 · 抽象工厂——何时用哪个?
工厂模式三种形态全解析简单工厂 · 工厂方法 · 抽象工厂——何时用哪个一、引言为什么创建对象也需要仪式感如果你曾经写过这样的代码——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

相关新闻

【大作业-63】基于YOLO26的交通手势识别系统

【大作业-63】基于YOLO26的交通手势识别系统

【大作业-63】基于YOLO26的交通手势识别系统 YOLO26模型介绍 YOLO26是Ultralytics于2026年发布的最新一代YOLO模型,代表了目标检测领域的重大技术突破 YOLO系列发展简史 YOLO的诞生与演进 YOLO(You Only Look Once)系列是计算机视觉领域最具…

2026/7/6 6:59:51 阅读更多 →
Redis 删了 2GB 数据,内存却纹丝不动?深挖内存碎片的真相

Redis 删了 2GB 数据,内存却纹丝不动?深挖内存碎片的真相

Redis 删了 2GB 数据,内存却纹丝不动?深挖内存碎片的真相你有没有遇到过这样诡异的场景:明明已经删除了大量 Key,用 top 命令一看,Redis 进程内存占用依然高居不下;或者 info memory 告诉你 Redis 存储的数…

2026/5/17 6:33:04 阅读更多 →
stm32是用杜邦线母头接核心板和调试器吗

stm32是用杜邦线母头接核心板和调试器吗

是的,必须用杜邦线母头(母对母,F-F)。因为核心板上的排针是公头(凸出来的针),DAP调试器上的接口也是公头,所以你需要用两头都是带锁扣(或不带锁扣)的母头杜邦…

2026/7/5 9:59:00 阅读更多 →

最新新闻

番茄小说下载器终极指南:从零开始打造个人数字图书馆的完整解决方案

番茄小说下载器终极指南:从零开始打造个人数字图书馆的完整解决方案

番茄小说下载器终极指南:从零开始打造个人数字图书馆的完整解决方案 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 还在为无法离线阅读番茄小说而烦恼吗&#xff…

2026/7/6 6:57:03 阅读更多 →
PCF8591与PIC18F46K80的信号转换系统设计与优化

PCF8591与PIC18F46K80的信号转换系统设计与优化

1. PCF8591与PIC18F46K80的信号转换系统概述在嵌入式系统开发中,模拟信号与数字信号的相互转换是常见需求。PCF8591作为一款集成了ADC和DAC功能的芯片,配合PIC18F46K80这款高性能8位单片机,可以构建一个灵活的信号处理系统。这个组合特别适合…

2026/7/6 6:57:02 阅读更多 →
参数检验 vs 非参数检验:5种常见场景下的选择决策树与Python/SPSS实现

参数检验 vs 非参数检验:5种常见场景下的选择决策树与Python/SPSS实现

参数检验 vs 非参数检验:5种常见场景下的选择决策树与Python/SPSS实现 数据分析的核心任务之一是通过样本数据推断总体特征。在这个过程中,统计检验方法的选择直接影响结论的可靠性。参数检验和非参数检验作为两大主流方法,各自适用于不同的数…

2026/7/6 6:53:01 阅读更多 →
Python 3.12 文本情感分析实战:基于BERT模型解析《母亲》主题情感倾向

Python 3.12 文本情感分析实战:基于BERT模型解析《母亲》主题情感倾向

Python 3.12 文本情感分析实战:基于BERT模型解析《母亲》主题情感倾向在当代自然语言处理领域,情感分析技术已成为理解文本深层含义的重要工具。本文将带您用Python 3.12和BERT模型,对经典文本《母亲》进行专业级情感倾向解析。不同于传统的人…

2026/7/6 6:53:01 阅读更多 →
LCD 液晶屏驱动时序详解:以 800x480 分辨率为例,配置 VBP/VFP/HBP/HFP 4 个关键参数

LCD 液晶屏驱动时序详解:以 800x480 分辨率为例,配置 VBP/VFP/HBP/HFP 4 个关键参数

LCD 液晶屏驱动时序深度解析:800x480 分辨率实战配置指南1. 液晶显示技术基础与驱动原理液晶显示器(LCD)作为现代电子设备最常用的显示技术之一,其核心在于通过电场精确控制液晶分子的排列状态。当我们在嵌入式系统中使用LCD时&am…

2026/7/6 6:53:01 阅读更多 →
SLO2016与PIC18F87J50在工业自动化中的高效组合

SLO2016与PIC18F87J50在工业自动化中的高效组合

1. SLO2016与PIC18F87J50的黄金组合解析在工业自动化领域,信号传输的稳定性和可靠性直接决定了整个系统的运行质量。SLO2016光电耦合器与PIC18F87J50微控制器的组合,正是为解决这一核心问题而生的经典方案。这套组合拳的独特之处在于:SLO2016…

2026/7/6 6:51:01 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻