当前位置: 首页 > 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 alipayFactorynew AlipayFactory();Payment alipayalipayFactory.createPayment();alipay.pay(100.0);alipay.refund(50.0);//2. 使用微信支付 PaymentFactory wechatPayFactorynew WechatPayFactory();Payment wechatPaywechatPayFactory.createPayment();wechatPay.pay(200.0);wechatPay.refund(100.0);//3. 使用银联支付 PaymentFactory unionPayFactorynew UnionPayFactory();Payment unionPayunionPayFactory.createPayment();unionPay.pay(300.0);unionPay.refund(150.0);}}运行结果使用支付宝支付100.0元 支付宝退款50.0元 使用微信支付200.0元 微信退款100.0元 使用银联支付300.0元 银联退款150.0元核心优势体现假设现在需要新增“京东支付”我们只需做两步无需修改任何原有代码新增具体产品JdPay类实现Payment接口新增具体工厂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.zskr.cn/news/1387057.html

相关文章:

  • 本地视频转文字完全免费教程:video2text实现离线语音转写+AI智能总结
  • 2026年4月评价高的弯头生产厂家推荐,石油套管/对焊弯头/法兰/船标法兰/高压法兰/管件/大小头,弯头源头厂家哪家好 - 品牌推荐师
  • Python asyncio 模块学习总结:从“等着”到“切出去干点别的”
  • 从ArcGIS Pro缓冲区分析到自定义工具:一个Add-in插件搞定你的自动化工作流
  • SemiTool 半导体设备上位机系统 - 软件开发文档
  • 从‘模拟器20开’到‘编译Android源码’:一台X99+E5-2696V3主机的多面手实战记录
  • 【CGLIB】为什么 Java 中已经有了 JDK 动态代理,还需要 CGLIB?两者最根本的区别在哪里?
  • Smardaten多维可视化大屏|全网独家实战,无代码极速搭建篇 引入多源数据融合+交互联动增强,助力企业级监控中心快速落地、效能翻倍
  • 使用 Taotoken 后 API 调用延迟与稳定性有哪些直观感受
  • Unlock Music终极指南:3分钟解锁加密音乐,实现真正的音乐自由
  • 从《原神》到独立游戏:拆解Unity帧更新(Update/FixedUpdate/LateUpdate)如何影响你的游戏手感与性能
  • Linux多线程编程(二):互斥锁与条件变量,手写生产者消费者模型
  • 字符串--- 最长公共前缀 | 最长回文子串 | 二进制求和
  • 深入解析 Android 系统启动流程:从开机到应用加载的全面指南
  • PDF 安全防护:打开密码设置与解除方法
  • 手把手教你:把阿里云RDS的物理备份文件(.xb)恢复到本地MySQL 5.7
  • JetPack6.2即ubuntu22.04安装firefox浏览器教程
  • C语言指针01
  • ELKStack高效部署与架构解析
  • 为什么苏州工厂老板都会选择响课教育做GEO优化?一文深度解读!
  • Claude Code 全栈提示词:前端/Java/UI/测试一册通
  • ARM调试状态核心机制与PSTATE处理详解
  • 告别手动选点:cam_lidar_calibration如何用VOQ自动筛选最优标定位姿?
  • 你的图片安全吗?聊聊LSB隐写的‘易碎性’和那些年我们踩过的坑
  • FlashAttention V3 前瞻:下一代Attention优化方向
  • 考研复习 Day 40 | 密码学--第四章 分组密码(中)
  • Linux运维之磁盘分区与挂载详解
  • TVA在电子元器件领域的创新应用(9)
  • 终极指南:如何在Mac上使用Topit实现300%效率提升的窗口置顶
  • 利用Taotoken模型广场为智能CRM选择合适的大模型