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

【设计模式-4】深入理解设计模式:工厂模式详解

在软件开发中,对象的创建是一个基础但至关重要的环节。随着系统复杂度的增加,直接使用new关键字实例化对象会带来诸多问题,如代码耦合度高、难以扩展和维护等。工厂模式(Factory Pattern)作为一种创建型设计模式,为我们提供了一种更优雅、更灵活的解决方案。本文将全面剖析工厂模式的三种形式:简单工厂、工厂方法和抽象工厂,帮助你掌握这一强大工具。

1. 工厂模式概述

1.1 什么是工厂模式

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。其核心思想是将对象的创建与使用分离,客户端无需关心对象的创建细节,只需通过工厂获取所需对象。

1.2 为什么需要工厂模式

在没有使用工厂模式的情况下,我们通常会这样创建对象:

Product product = new ConcreteProduct();

这种方式存在以下问题:

  • 客户端代码与具体实现类紧耦合
  • 当创建逻辑复杂时,代码重复且难以维护
  • 新增产品类型时需要修改多处客户端代码

工厂模式通过封装对象的创建过程,解决了这些问题,提供了以下优势:

  • 解耦:将对象创建与使用分离
  • 可扩展性:易于添加新产品类型
  • 可维护性:集中管理对象的创建逻辑
  • 灵活性:可以轻松替换或修改创建逻辑

2. 简单工厂模式(Simple Factory)

2.1 定义与结构

简单工厂模式又称为静态工厂方法模式,它定义一个工厂类,根据传入的参数不同返回不同类型的实例。

结构组成

  • Factory:工厂类,负责创建产品
  • Product:抽象产品类/接口
  • ConcreteProduct:具体产品类

2.2 代码示例

// 抽象产品
interface Product {void operation();
}// 具体产品A
class ConcreteProductA implements Product {@Overridepublic void operation() {System.out.println("ConcreteProductA operation");}
}// 具体产品B
class ConcreteProductB implements Product {@Overridepublic void operation() {System.out.println("ConcreteProductB operation");}
}// 简单工厂
class SimpleFactory {public static Product createProduct(String type) {switch (type) {case "A":return new ConcreteProductA();case "B":return new ConcreteProductB();default:throw new IllegalArgumentException("Unknown product type");}}
}// 客户端代码
public class Client {public static void main(String[] args) {Product productA = SimpleFactory.createProduct("A");productA.operation();  // 输出: ConcreteProductA operationProduct productB = SimpleFactory.createProduct("B");productB.operation();  // 输出: ConcreteProductB operation}
}

2.3 优缺点分析

优点

  • 客户端与具体产品解耦
  • 集中管理对象的创建逻辑

缺点

  • 工厂类职责过重,违反单一职责原则
  • 新增产品类型需要修改工厂类,违反开闭原则
  • 静态方法无法通过继承改变创建行为

2.4 适用场景

  • 产品类型较少且不太可能变化
  • 客户端不关心对象的创建细节
  • 需要集中管理对象的创建逻辑

3. 工厂方法模式(Factory Method)

3.1 定义与结构

工厂方法模式定义了一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

结构组成

  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Creator:抽象工厂
  • ConcreteCreator:具体工厂

3.2 代码示例

// 抽象产品
interface Product {void operation();
}// 具体产品A
class ConcreteProductA implements Product {@Overridepublic void operation() {System.out.println("ConcreteProductA operation");}
}// 具体产品B
class ConcreteProductB implements Product {@Overridepublic void operation() {System.out.println("ConcreteProductB operation");}
}// 抽象工厂
interface Factory {Product createProduct();
}// 具体工厂A
class ConcreteFactoryA implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductA();}
}// 具体工厂B
class ConcreteFactoryB implements Factory {@Overridepublic Product createProduct() {return new ConcreteProductB();}
}// 客户端代码
public class Client {public static void main(String[] args) {Factory factoryA = new ConcreteFactoryA();Product productA = factoryA.createProduct();productA.operation();  // 输出: ConcreteProductA operationFactory factoryB = new ConcreteFactoryB();Product productB = factoryB.createProduct();productB.operation();  // 输出: ConcreteProductB operation}
}

3.3 优缺点分析

优点

  • 完全遵循开闭原则,新增产品类型只需添加新工厂类
  • 客户端代码与具体产品完全解耦
  • 符合单一职责原则,每个工厂只负责创建一种产品

缺点

  • 类的数量增加,系统复杂度提高
  • 增加了抽象层,理解难度增加

3.4 适用场景

  • 客户端不知道它所需要的对象的类
  • 需要灵活、可扩展的系统
  • 将产品创建延迟到子类实现

4. 抽象工厂模式(Abstract Factory)

4.1 定义与结构

抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

结构组成

  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • ConcreteProduct:具体产品

4.2 代码示例

// 抽象产品A
interface ProductA {void operationA();
}// 具体产品A1
class ConcreteProductA1 implements ProductA {@Overridepublic void operationA() {System.out.println("ConcreteProductA1 operation");}
}// 具体产品A2
class ConcreteProductA2 implements ProductA {@Overridepublic void operationA() {System.out.println("ConcreteProductA2 operation");}
}// 抽象产品B
interface ProductB {void operationB();
}// 具体产品B1
class ConcreteProductB1 implements ProductB {@Overridepublic void operationB() {System.out.println("ConcreteProductB1 operation");}
}// 具体产品B2
class ConcreteProductB2 implements ProductB {@Overridepublic void operationB() {System.out.println("ConcreteProductB2 operation");}
}// 抽象工厂
interface AbstractFactory {ProductA createProductA();ProductB createProductB();
}// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA1();}@Overridepublic ProductB createProductB() {return new ConcreteProductB1();}
}// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA2();}@Overridepublic ProductB createProductB() {return new ConcreteProductB2();}
}// 客户端代码
public class Client {public static void main(String[] args) {AbstractFactory factory1 = new ConcreteFactory1();ProductA productA1 = factory1.createProductA();ProductB productB1 = factory1.createProductB();productA1.operationA();  // 输出: ConcreteProductA1 operationproductB1.operationB();  // 输出: ConcreteProductB1 operationAbstractFactory factory2 = new ConcreteFactory2();ProductA productA2 = factory2.createProductA();ProductB productB2 = factory2.createProductB();productA2.operationA();  // 输出: ConcreteProductA2 operationproductB2.operationB();  // 输出: ConcreteProductB2 operation}
}

4.3 优缺点分析

优点

  • 保证产品族的一致性
  • 将具体产品与客户端代码分离
  • 易于交换产品系列
  • 符合开闭原则(扩展新产品族容易)

缺点

  • 难以支持新种类的产品(违反开闭原则)
  • 类数量爆炸性增长
  • 增加了系统的抽象性和理解难度

4.4 适用场景

  • 系统需要一系列相关产品
  • 需要提供一个产品类库,且只暴露接口
  • 产品族需要一起使用,确保一致性
  • 需要动态切换产品族

5. 三种工厂模式对比

特性简单工厂模式工厂方法模式抽象工厂模式
复杂度
适用场景简单对象创建单一产品创建产品族创建
扩展性差(需修改工厂类)好(新增工厂类)较好(新增工厂类)
开闭原则违反遵循部分遵循
类数量
产品一致性不涉及不涉及保证产品族一致性

6. 工厂模式在实际中的应用

6.1 JDK中的工厂模式

  • java.util.Calendar#getInstance()
  • java.text.NumberFormat#getInstance()
  • java.nio.charset.Charset#forName()

6.2 Spring框架中的工厂模式

  • BeanFactory:Spring容器的根接口
  • ApplicationContext:应用上下文接口
  • FactoryBean:特殊bean的工厂接口

6.3 实际案例:数据库连接工厂

// 数据库连接工厂接口
interface ConnectionFactory {Connection createConnection();Statement createStatement();
}// MySQL连接工厂
class MySQLConnectionFactory implements ConnectionFactory {@Overridepublic Connection createConnection() {return new MySQLConnection();}@Overridepublic Statement createStatement() {return new MySQLStatement();}
}// Oracle连接工厂
class OracleConnectionFactory implements ConnectionFactory {@Overridepublic Connection createConnection() {return new OracleConnection();}@Overridepublic Statement createStatement() {return new OracleStatement();}
}

7. 工厂模式的最佳实践

  1. 优先使用工厂方法模式:在大多数情况下,工厂方法模式比简单工厂更灵活,比抽象工厂更简单
  2. 考虑使用依赖注入:结合Spring等框架的DI容器可以更好地管理工厂和产品
  3. 避免过度设计:简单场景下直接使用new可能更合适
  4. 命名约定:工厂类名通常以"Factory"结尾,方法名通常为"createXxx"或"getInstance"
  5. 文档化:为工厂类和产品类添加充分的文档说明

8. 常见问题与解决方案

Q1:什么时候该用抽象工厂而不是工厂方法?

当需要创建一组相关或依赖的对象时使用抽象工厂,如果只是创建单个对象,使用工厂方法更合适。

Q2:工厂模式会增加很多类,如何管理?

可以使用包结构组织相关工厂和产品类,或者考虑使用模块化系统。

Q3:如何处理工厂创建对象的失败情况?

可以在工厂方法中抛出异常或返回null,但更好的做法是使用Optional或特定结果对象包装返回结果。

Q4:如何选择三种工厂模式?

从简单工厂开始,当需要更多灵活性时升级到工厂方法,当需要管理产品族时再考虑抽象工厂。

9. 结语

工厂模式是面向对象设计中极其重要的模式之一,它通过封装对象的创建过程,实现了创建与使用的分离,大大提高了代码的灵活性和可维护性。理解并合理运用工厂模式,能够帮助你构建更加健壮、可扩展的软件系统。

相关文章:

【设计模式-4】深入理解设计模式:工厂模式详解

在软件开发中,对象的创建是一个基础但至关重要的环节。随着系统复杂度的增加,直接使用new关键字实例化对象会带来诸多问题,如代码耦合度高、难以扩展和维护等。工厂模式(Factory Pattern)作为一种创建型设计模式&#…...

Spring Boot 整合 JavaFX 核心知识点详解

1. 架构设计与集成模式 1.1 Spring Boot 与 JavaFX 的分层架构设计 Spring Boot 与 JavaFX 的整合需要精心设计的分层架构,以充分利用两个框架的优势。 标准分层架构 ┌────────────────────────────────────────────────…...

Spring MVC DispatcherServlet 的作用是什么? 它在整个请求处理流程中扮演了什么角色?为什么它是核心?

DispatcherServlet 是 Spring MVC 框架的绝对核心和灵魂。它扮演着前端控制器(Front Controller)的角色,是所有进入 Spring MVC 应用程序的 HTTP 请求的统一入口点和中央调度枢纽。 一、 DispatcherServlet 的核心作用和职责: 请…...

亚马逊英国站FBA费用重构:轻小商品迎红利期,跨境卖家如何抢占先机?

一、政策背景:成本优化成平台与卖家共同诉求 2024年4月,亚马逊英国站(Amazon.co.uk)发布近三年来力度最大的FBA费用调整方案,标志着英国电商市场正式进入精细化成本管理时代。这一决策背后,是多重因素的叠…...

Redis在.NET平台中的各种应用场景

关键点总结 连接管理:所有示例都使用ConnectionMultiplexer来管理Redis连接,它是线程安全的,应该在整个应用程序中重用。 键设计:良好的键命名规范很重要,通常使用冒号分隔的层次结构(如page:home:pv)。 数据序列化&…...

近几年字节测开部分面试题整理

文章目录 前言一、面试问题1. 在浏览器url上写一个地址,请描述一下网络方面有哪些变化2. 堆栈数据存储位置3. HTTP POST请求支持的数据格式4. 缩容要注意些什么?5. Python中元组、数组、list和数组的区别6. Python中is和的区别7. HTTP与HTTPS8. 已知两个…...

艾蒙顿桌面app下载-Emotn UI下载安装-emotn ui官方tv版安卓固件

在智能电视桌面应用的领域里,Emotn UI 凭借其简洁无广告、可自定义等特点,赢得了不少用户的关注。然而,小编深入了解后发现了一款更好用的电视桌面——乐看家桌面在诸多方面更具优势,能为你带来更优质的大屏体验。 乐看家桌面内置…...

3、ArkTS语言介绍

目录 基础知识函数函数声明可选参数Rest参数返回类型箭头函数(又名Lambda函数)闭包 类字段字段初始化getter和setter继承父类访问方法重写方法重载签名可见性修饰符(Public、Private、protected) 基础知识 ArkTS是一种为构建高性…...

修改了Element UI中组件的样式,打包后样式丢失

修改了Element UI中组件的样式,在本地运行没有问题,但是打包到线上发现样式丢失(样式全部不生效、或者有一部分生效,一部分不生效),问题在于css的加载顺序导致代码编译后样式被覆盖了, 解决办法…...

Linux GPIO驱动开发实战:Poll与异步通知双机制详解

1. 引言 在嵌入式Linux开发中,GPIO按键驱动是最基础也最典型的案例之一。本文将基于一个支持poll和异步通知双机制的GPIO驱动框架,深入剖析以下核心内容: GPIO中断与防抖处理环形缓冲区设计Poll机制实现异步通知(SIGIO)实现应用层交互方式 …...

【springsecurity oauth2授权中心】jwt令牌更换成自省令牌 OpaqueToken P4

前言 前面实现了授权中心授权,客户端拿到access_token后就能请求资源服务器接口 权限的校验都是在资源服务器上进行的,授权服务器颁发的access_token有限期是2小时,也就是说在2小时之内,不管授权服务器那边用户的权限如何变更都…...

诱骗协议芯片支持PD2.0/3.0/3.1/PPS协议,支持使用一个Type-C与电脑传输数据和快充取电功能

快充是由充电器端的充电协议和设备端的取电协议进行握手通讯进行协议识别来完成的,当充电器端的充电协议和设备端的取电协议握手成功后,设备会向充电器发送电压请求,充电器会根据设备的需求发送合适的电压给设备快速供电。 设备如何选择快充…...

变量在template里不好使,在setup好使?

问题: 自定义的一个函数 ,import导入后 setup里面使用正常 ,在template里面说未定义 作用域问题 在 Vue 的模板语法中,模板(template )里能直接访问的是组件实例上暴露的属性和方法。从代码看&#xff0c…...

OpenCV 图形API(53)颜色空间转换-----将 RGB 图像转换为灰度图像函数RGB2Gray()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像从 RGB 色彩空间转换为灰度。 R、G 和 B 通道值的常规范围是 0 到 255。生成的灰度值计算方式如下: dst ( I ) 0.299 ∗ src…...

Trae+DeepSeek学习Python开发MVC框架程序笔记(四):使用sqlite存储查询并验证用户名和密码

继续通过Trae向DeepSeek发问并修改程序,实现程序运行时生成数据库,用户在系统登录页面输入用户名和密码后,控制器通过模型查询用户数据库表来验证用户名和密码,验证通过后显示登录成功页面,验证失败则显示登录失败页面…...

超详细mac上用nvm安装node环境,配置npm

一、安装NVM 打开终端,运行以下命令来安装NVM: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash 然后就会出现如下代码: > Profile not found. Tried ~/.bashrc, ~/.bash_profile, ~/.zprofile, ~/.…...

STM32 串口通信

引言 在嵌入式系统开发中,串口通信是最基础且重要的通信方式之一。无论是设备调试、模块对接还是远程通信,串口都扮演着关键角色。本文将从通信协议原理出发,结合STM32F4系列MCU,深入讲解串口通信的硬件实现和软件配置&#xff0…...

hi3516cv610构建音频sample工程代码步骤

hi3516cv610构建音频sample工程代码步骤 sdk版本:Hi3516CV610_SDK_V1.0.1.0 硬件:非es8388 工程代码: 通过网盘分享的文件:audio_easy.zip 链接: https://pan.baidu.com/s/1gx61S_F3-pf6hPyfbGaRXg 提取码: 4gbg --来自百度网盘…...

12.QT-Combo Box|Spin Box|模拟点餐|从文件中加载选项|调整点餐份数(C++)

Combo Box QComboBox 表⽰下拉框 核⼼属性 属性说明currentText当前选中的⽂本currentIndex当前选中的条⽬下标.从0开始计算.如果当前没有条⽬被选中,值为-1editable是否允许修改设为true时, QComboBox 的⾏为就⾮常接近 QLineEdit ,也可以 设置 validatoriconSize下拉框图标…...

UML 顺序图:电子图书馆管理系统的交互之道

目录 一、初识 UML 顺序图 二、电子图书馆管理系统顺序图解析 (一)借阅流程 (二)归还流程 三、顺序图绘画 四、顺序图的优势与价值 五、总结 UML 顺序图是描绘系统组件交互的有力工具。顺序图直观展示消息传递顺序与对象协…...

分布式架构设计与应用:从理论到实践

在云计算、大数据与高并发场景的驱动下,分布式架构已成为现代软件系统的核心技术。它通过将计算、存储与业务逻辑分散到多台机器上,解决了单体架构的扩展性瓶颈与单点故障问题。本文将从设计原则、核心组件到典型应用场景,深入剖析分布式架构…...

Uniapp:view容器(容器布局)

目录 一、基本概述二、属性说明三、常用布局3.1 横向布局3.2 纵向布局3.3 更多布局3.3.1 纵向布局-自动宽度3.3.2 纵向布局-固定宽度3.3.3 横向布局-自动宽度3.3.4 横向布局-居中3.3.5 横向布局-居右3.3.6 横向布局-平均分布3.3.7 横向布局-两端对齐3.3.8 横向布局-自动填充3.3…...

访问者模式:分离数据结构与操作的设计模式

访问者模式:分离数据结构与操作的设计模式 一、模式核心:将操作从数据结构中分离,支持动态添加新操作 在软件开发中,当数据结构(如树、集合)中的元素类型固定,但需要频繁添加新的操作&#xf…...

【AI训练环境搭建】在IDE(Pycharm或VSCode)上使用WSL2+Ubuntu22.04+Conda+Tensorflow+GPU进行机器学习训练

本次实践将在IDE(Pycharm或VSCode)上使用WSL2Ubuntu22.04TensorflowGPU进行机器学习训练。基本原理是在IDE中拉起WSL2中的Python解释器,并运行Python程序。要运行CondaTensorflowGPU你可能需要进行以下准备工作。 1. 此示例中将使用一个mnis…...

Leetcode19(亚马逊真题):删除链表的倒是第N个节点

题目分析 删除节点关键:找到被删节点的前一个节点,指针指向 虚拟头节点,方便删除头结点,形成统一操作 为啥要让快指针先行? 我认为更好懂的一种解释:快指针先行n步,这样快慢指针之间形成了一…...

Hadoop+Spark 笔记 2025/4/21

读书笔记 定义 1. 大数据(Big Data) - 指传统数据处理工具难以处理的海量、高速、多样的数据集合,通常具备3V特性(Volume体量大、Velocity速度快、Variety多样性)。扩展后还包括Veracity(真实性&#x…...

千问2.5-VL-7B的推理、微调、部署_笔记2

接上篇:部署千问2.5-VL-7B_笔记1-CSDN博客 这里主要记录微调过程 一、模型微调 这里也使用ms-swift对qwen2.5和qwen2-vl进行自我认知微调和图像OCR微调,并对微调后的模型进行推理。ms-swift是魔搭社区官方提供的LLM工具箱,支持300大语言模…...

Redis从入门到实战基础篇

前言:Redis的安装包含在Redis从入门到实战先导篇中,需要的可移步至此节 目录 1.Redis简单介绍 2.初始Redis 2.1.认识NoSQL 2.2.认识Redis 2.3.安装Redis 3.Redis常见命令 3.1 Redis数据结构 3.2 通用命令 3.3 String命令 3.4 Key的层级结构 3…...

【Docker】在Ubuntu平台上的安装部署

写在前面 docker作为一种部署项目的辅助工具,真是太好用了需要魔法,不然无法正常运行笔者环境:ubuntu22.04 具体步骤 更新系统包索引 sudo apt update安装必要依赖包 sudo apt install -y apt-transport-https ca-certificates curl softwa…...

Java虚拟机(JVM)家族发展史及版本对比

Java虚拟机(JVM)家族发展史及版本对比 一、JVM家族发展史 1. 早期阶段(1996-2000) Classic VM(Java 1.0-1.1): 厂商:Sun Microsystems(Oracle前身)。特点&…...