当前位置: 首页 > news >正文

设计模式系列文章(基础篇第 3 篇):工厂方法模式——解耦对象创建与使用

大家好,欢迎来到设计模式系列文章(基础篇)的第三篇内容。在上一篇中,我们学习了创建型模式的第一种——单例模式,它的核心是保证“全局唯一实例”;而今天我们要学习的工厂方法模式,同样属于创建型模式,但其核心目标更偏向于“解耦”——将对象的创建过程与使用过程分离,让对象的创建交由专门的“工厂”负责,我们无需关注对象如何创建,只需专注于对象的使用,从而提升代码的灵活性和可扩展性。
在正式讲解之前,我们先想一个日常开发中的常见场景:假设我们要开发一个支付系统,支持支付宝、微信支付、银联支付三种支付方式。如果不使用设计模式,我们会在代码中直接通过new关键字创建不同的支付对象,比如需要支付宝支付就new Alipay(),需要微信支付就new WechatPay()。这种方式看似简单,但存在一个严重的问题:如果后续需要新增支付方式(如京东支付),就必须修改所有创建支付对象的代码,不仅繁琐,还会导致代码耦合度极高,不符合“开闭原则”。
而工厂方法模式,就完美解决了这个问题。它通过引入“工厂”角色,将对象的创建逻辑封装在工厂中,我们只需调用工厂的方法,就能获取所需的对象,无需关心对象的创建细节。后续新增对象类型时,只需新增对应的工厂,无需修改原有代码,真正实现“对扩展开放、对修改关闭”。今天,我们就从工厂方法模式的核心思想出发,讲解它的定义、结构、实现方式,以及与简单工厂模式的区别,帮大家彻底掌握这种常用的创建型模式。

一、工厂方法模式的核心定义与设计初衷

1. 核心定义
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
通俗地说,工厂方法模式就像一个“对象生产车间”:我们定义一个统一的“工厂接口”,规定所有工厂都必须实现“生产对象”的方法;然后为每一种对象类型,创建一个对应的“具体工厂”,由具体工厂负责生产对应的对象。我们使用时,只需找到对应的具体工厂,调用生产方法,就能得到所需对象,无需关心对象的创建细节。

2. 设计初衷(解决的核心问题)
工厂方法模式的出现,主要是为了解决“简单工厂模式”的缺陷(后续会对比两者),核心解决以下3个问题:

  • 解耦对象创建与使用:将对象创建逻辑从业务代码中分离,业务代码只需使用对象,无需关注创建过程,降低代码耦合度;
  • 满足开闭原则:新增对象类型时,无需修改原有工厂代码,只需新增具体对象类和对应的具体工厂,扩展性强;
  • 统一对象创建标准:通过工厂接口规范所有工厂的行为,确保所有对象的创建都遵循统一的标准,提升代码的规范性和可维护性。

3. 设计原则适配
工厂方法模式严格遵循创建型模式的核心设计原则,重点体现以下3点:

  • 开闭原则:新增产品(对象)时,只需新增具体产品类和具体工厂类,无需修改原有工厂接口和业务代码,对扩展开放、对修改关闭;
  • 单一职责原则:每个具体工厂只负责生产一种具体产品,每个产品类只负责自身的业务逻辑,职责单一,便于维护;
  • 依赖倒转原则:业务代码依赖于工厂接口和产品接口,而非具体的工厂类和产品类,降低了依赖的耦合度,提升了代码的灵活性。

二、工厂方法模式的核心结构(4个角色)

工厂方法模式有4个核心角色,缺一不可,它们相互配合,实现对象的创建与使用分离。我们结合支付系统的场景,逐一讲解每个角色的作用:
1. 抽象产品(Abstract Product)
定义所有具体产品(对象)的共同接口或抽象类,规范产品的核心行为。比如支付系统中,所有支付方式都有“支付”“退款”两个核心行为,因此抽象产品就是“支付接口”,定义这两个方法。

2. 具体产品(Concrete Product)
实现抽象产品接口,是工厂方法模式最终要创建的对象。比如支付系统中的“支付宝支付”“微信支付”“银联支付”,都是具体产品,它们分别实现抽象产品的“支付”“退款”方法,实现各自的业务逻辑。

3. 抽象工厂(Abstract Factory)
定义一个用于创建产品的接口,包含一个“创建产品”的抽象方法,所有具体工厂都必须实现这个接口。比如支付系统中的“支付工厂接口”,定义一个“创建支付对象”的方法。

4. 具体工厂(Concrete Factory)
实现抽象工厂接口,负责创建对应的具体产品。比如“支付宝工厂”负责创建支付宝支付对象,“微信支付工厂”负责创建微信支付对象,每个具体工厂只对应一种具体产品。
核心关系总结:抽象工厂定义创建产品的标准,具体工厂实现标准并创建具体产品,具体产品实现抽象产品的行为,业务代码通过调用具体工厂的方法,获取具体产品并使用。

三、工厂方法模式的实战实现(以支付系统为例)

我们依然以Java代码为例,结合支付系统的场景,一步步实现工厂方法模式,让大家直观理解每个角色的作用和代码落地方式。
1. 第一步:定义抽象产品(支付接口)
定义所有支付方式的共同接口,规范“支付”和“退款”两个核心行为:

// 抽象产品:支付接口 public interface Payment{// 支付方法 void pay(double amount);// 退款方法 void refund(double amount);}

2. 第二步:实现具体产品(具体支付方式)
分别实现支付宝支付、微信支付、银联支付,各自实现支付和退款的具体逻辑:

// 具体产品1:支付宝支付 public class Alipay implements Payment{@Override public void pay(double amount){System.out.println("使用支付宝支付:"+ amount +"元");}@Override public void refund(double amount){System.out.println("支付宝退款:"+ amount +"元");}}// 具体产品2:微信支付 public class WechatPay implements Payment{@Override public void pay(double amount){System.out.println("使用微信支付:"+ amount +"元");}@Override public void refund(double amount){System.out.println("微信退款:"+ amount +"元");}}// 具体产品3:银联支付 public class UnionPay implements Payment{@Override public void pay(double amount){System.out.println("使用银联支付:"+ amount +"元");}@Override public void refund(double amount){System.out.println("银联退款:"+ amount +"元");}}

3. 第三步:定义抽象工厂(支付工厂接口)
定义创建支付对象的接口,包含一个抽象的创建方法:

// 抽象工厂:支付工厂接口 public interface PaymentFactory{// 抽象创建方法:返回支付对象 Payment createPayment();}

4. 第四步:实现具体工厂(对应具体支付方式)
为每种支付方式创建对应的具体工厂,实现抽象工厂的创建方法,返回对应的具体支付对象:

// 具体工厂1:支付宝工厂 public class AlipayFactory implements PaymentFactory{@Override public PaymentcreatePayment(){// 负责创建支付宝支付对象returnnew Alipay();}}// 具体工厂2:微信支付工厂 public class WechatPayFactory implements PaymentFactory{@Override public PaymentcreatePayment(){// 负责创建微信支付对象returnnew WechatPay();}}// 具体工厂3:银联支付工厂 public class UnionPayFactory implements PaymentFactory{@Override public PaymentcreatePayment(){// 负责创建银联支付对象returnnew UnionPay();}}

5. 第五步:业务代码调用(使用工厂创建对象)
业务代码无需直接new支付对象,只需创建对应的具体工厂,调用createPayment()方法,即可获取支付对象并使用,实现创建与使用分离:

// 业务代码测试 public class PaymentTest{public static void main(String[]args){//1. 使用支付宝支付 PaymentFactory alipayFactory=new AlipayFactory();Payment alipay=alipayFactory.createPayment();alipay.pay(100.0);alipay.refund(50.0);//2. 使用微信支付 PaymentFactory wechatPayFactory=new WechatPayFactory();Payment wechatPay=wechatPayFactory.createPayment();wechatPay.pay(200.0);wechatPay.refund(100.0);//3. 使用银联支付 PaymentFactory unionPayFactory=new UnionPayFactory();Payment unionPay=unionPayFactory.createPayment();unionPay.pay(300.0);unionPay.refund(150.0);}}

运行结果

使用支付宝支付:100.0元 支付宝退款:50.0元 使用微信支付:200.0元 微信退款:100.0元 使用银联支付:300.0元 银联退款:150.0元

核心优势体现
假设现在需要新增“京东支付”,我们只需做两步,无需修改任何原有代码:

  1. 新增具体产品:JdPay类,实现Payment接口;
  2. 新增具体工厂:JdPayFactory类,实现PaymentFactory接口,返回JdPay对象。
    这种方式完全符合开闭原则,扩展性极强,且业务代码无需任何修改,只需新增对应的工厂调用即可。

四、工厂方法模式 vs 简单工厂模式(重点区分)

很多人会混淆工厂方法模式和简单工厂模式,两者都属于创建型模式,核心都是解耦对象创建与使用,但存在本质区别。我们通过表格清晰对比,帮大家快速区分:

对比维度工厂方法模式简单工厂模式
核心结构抽象工厂+具体工厂+抽象产品+具体产品(4个角色)单一工厂+抽象产品+具体产品(3个角色)
对象创建逻辑每个具体工厂负责创建一种具体产品,职责单一一个工厂负责创建所有具体产品,逻辑集中
扩展性强,新增产品只需新增具体产品和具体工厂,无需修改原有代码(符合开闭原则)弱,新增产品需修改工厂的创建逻辑(不符合开闭原则)
复杂度稍高,需要定义多个工厂类简单,只需一个工厂类,代码简洁
适用场景产品种类多、易扩展,对扩展性要求高的场景产品种类少、变化少,对扩展性要求低的场景

补充说明:简单工厂模式是工厂方法模式的“简化版”,虽然简单,但扩展性差;工厂方法模式是简单工厂模式的“升级版”,通过拆分工厂职责,提升了扩展性,是日常开发中更推荐的方式。

五、工厂方法模式的常见应用场景

工厂方法模式的应用非常广泛,尤其是在产品种类多、易扩展的场景中,以下是3个最常见的实际应用场景,帮大家更好地落地:
1. 框架中的应用
很多主流框架都大量使用了工厂方法模式,比如Spring框架中的BeanFactory:

  • 抽象工厂:BeanFactory(定义创建Bean的接口);
  • 具体工厂:XmlBeanFactory、AnnotationConfigBeanFactory(不同的Bean创建工厂);
  • 抽象产品:Bean(所有Spring管理的对象);
  • 具体产品:我们定义的Service、Dao等类。
    我们通过Spring获取Bean时,无需关心Bean的创建过程,只需通过BeanFactory(或ApplicationContext)的方法获取,这就是工厂方法模式的典型应用。

2. 业务系统中的产品扩展场景
除了支付系统,以下场景也非常适合使用工厂方法模式:

  • 日志系统:支持文件日志、控制台日志、数据库日志,每种日志对应一个具体工厂;
  • 消息推送系统:支持短信推送、邮件推送、微信推送,每种推送方式对应一个具体工厂;
  • 文档解析系统:支持PDF解析、Word解析、Excel解析,每种解析方式对应一个具体工厂。

3. 工具类的统一创建场景
当一个系统中有多种功能相似但实现不同的工具类时,使用工厂方法模式可以统一创建入口,方便管理和扩展。比如文件操作工具,支持本地文件操作、FTP文件操作、云存储文件操作,每种操作对应一个具体工厂,业务代码可以灵活切换不同的文件操作方式。

六、工厂方法模式的常见坑与避坑指南

坑1:过度设计,滥用工厂方法模式
很多开发者为了追求“设计模式”,即使产品种类很少、几乎不会扩展,也强行使用工厂方法模式,导致代码冗余(需要定义多个工厂类)。比如一个系统只支持一种支付方式,却依然定义抽象工厂、具体工厂,完全没有必要。
避坑指南:如果产品种类少、变化少,优先使用简单工厂模式;只有当产品种类多、易扩展,且对扩展性要求高时,再使用工厂方法模式。

坑2:具体工厂职责不单一
部分开发者会在具体工厂中添加额外的业务逻辑(如支付工厂中添加支付验证逻辑),导致工厂职责不单一,违反“单一职责原则”,后续维护困难。
避坑指南:工厂的唯一职责就是“创建对象”,业务逻辑应放在具体产品类中,工厂只负责对象的实例化,不承担其他业务功能。

坑3:忽略抽象产品的设计
如果抽象产品定义的方法不够全面,后续新增具体产品时,可能需要修改抽象产品接口,违反开闭原则。比如支付接口只定义了pay()方法,后续新增的支付方式需要refund()方法,就必须修改抽象产品接口。
避坑指南:设计抽象产品时,要充分考虑所有具体产品的共同行为,提前定义好核心方法,避免后续修改抽象产品。

七、系列文章预告

本篇文章,我们详细讲解了工厂方法模式的核心定义、结构、实战实现,以及与简单工厂模式的区别,同时分享了常见坑与应用场景。相信大家已经能熟练运用工厂方法模式,解决“对象创建与使用分离”的需求,提升代码的扩展性和可维护性。
作为创建型模式的第二种常用模式,工厂方法模式解决了简单工厂模式的扩展性问题,但它依然存在一个局限:一个具体工厂只能创建一种具体产品。如果我们需要创建一组相关联的产品(比如电脑的CPU、显卡、内存,它们是一组关联产品),工厂方法模式就无法满足需求。
下一篇,我们将学习创建型模式的第三种——抽象工厂模式,它将解决“一组关联产品的创建”问题,进一步提升代码的设计能力。

http://www.cnnetsun.cn/news/2568236.html

相关文章:

  • Windows Server 2012 R2 下 VisualSVN Server 4.2.2 集成 Apache 与 PHP 实现 Web 端密码自助修改
  • 微信单向好友检测终极教程:WechatRealFriends免费工具完整使用指南
  • ROS1 Action通信避坑指南:手把手教你配置CMakeLists.txt和解决常见编译错误
  • 告别Unity默认Text!手把手教你用TextMeshPro打造炫酷UI文字(附中文字体制作避坑指南)
  • 文员转行AI应用岗,薪资涨了40%的真实路径,我的能力补齐清单
  • 别再浪费磁盘空间了!手把手教你用LVM精简卷(Thin Provisioning)给服务器‘瘦身’
  • AI 安全与对齐:2026年,大模型安全从“选修课“变成“必修课“
  • LLM推理系统优化:KV缓存管理与动态批处理技术
  • 超导量子计算机性能优化路线与关键技术
  • 别再傻傻分不清了!5分钟搞懂点乘和叉乘在游戏开发里的实际用法(Unity/C#)
  • 避坑指南:Calibre LVS验证中‘虚拟连接’、‘LVS BOX’和门级匹配的那些事儿
  • 国产化环境实战:在麒麟V10上为达梦DM8数据库配置ODBC驱动(附ARM/X86双架构配置差异)
  • RTKLIB LAMBDA算法实战:手把手教你用C++复现整周模糊度固定(附完整代码)
  • Unity角色移动原理与四大实现方案详解
  • 思源宋体完全指南:如何免费获得专业级中文字体体验?
  • LVGUI开发提速秘籍:用NXP GUI Guider设计界面,再一键移植到Keil工程(STM32/HC32通用)
  • Sentinel-3B OLCI 3 级全球分箱地球观测降分辨率(ERR)叶绿素(CHL)数据,版本 2022.0
  • 如何快速解决C盘爆红问题:Windows Cleaner免费系统优化工具完全指南
  • 用C语言解决‘换硬币’问题?我来教你如何调试和验证你的循环逻辑
  • 量子退火增强机器学习:高熵合金相预测的可解释性突破
  • 融合梯度加权PINNs与贝叶斯推断,攻克PDE反问题中的系数跳变识别难题
  • Sora 2 AVI支持背后的真相:为什么官方文档未声明?——基于逆向SDK v2.1.3a的ABI级分析(含AVI RIFF Chunk解析图谱)
  • 酒店门锁V10SDK接口说明-幽冥大陆(一百23)—东方仙盟
  • OpenCV连通域分析实战:手把手教你用C++实现Two-Pass算法(附完整代码)
  • DMA-330地址空间限制与扩展方案解析
  • ③ AI副业第一步:如何找到适合自己的AI赚钱赛道
  • DeepSeek系统设计辅助效能断崖式下降的3个信号,第2个90%工程师至今未察觉!
  • 告别printf小数精度烦恼:手把手教你用C语言实现真正的四舍五入(附完整代码)
  • 从STM32迁移到普冉PY32F003:UART代码移植保姆级教程(附HAL库对比)
  • 告别手写代码:用达芬奇Configurator+DBC文件,5分钟搞定AUTOSAR CAN通信基础配置