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

设计模式基础——工厂模式剖析(2/2)

目录

一、工厂模式

1.1 工厂模式的定义

1.2 工厂模式的设计意图

1.3 工厂模式主要解决的问题

1.4 工厂模式的缺点

1.5 实际的应用案例

1. 数据库连接池

2. 图形用户界面(GUI)组件

3. 文件操作

二、各种工厂模式的变形

1.1 简单工厂模式:

1.2 工厂方法模式:

1.3 抽象工厂模式

三、基本的代码实现


今天重点梳理下设计模式中,工厂模式的相关知识点。

一、工厂模式

1.1 工厂模式的定义

工厂模式(Factory Pattern)最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1.2 工厂模式的设计意图

工厂模式的主要设计意图是将对象的创建过程与使用过程分离,从而使得客户端代码无需知道具体创建什么类型的对象。这种分离有助于降低系统的耦合度,提高代码的可维护性和扩展性。

工厂模式通过以下方式实现这一目标:

1. 解耦对象的创建
   - 工厂模式允许将对象的创建过程封装在一个单独的类或一组类中(即工厂),而不是直接在客户端代码中实例化对象。
   - 这样可以避免在客户端代码中硬编码具体的对象类型,使得代码更加灵活和可复用。

2. 支持多种产品类型
   - 工厂模式可以根据不同的情况动态地选择要创建的对象类型,而客户端只需关心如何使用产品,而不必关心产品的具体实现细节。
   - 这种灵活性使得工厂模式适用于需要根据运行时条件来决定创建哪种产品的情况。

3. 隐藏复杂的产品创建逻辑
   - 如果对象的创建过程非常复杂或者涉及到多个步骤,工厂模式可以通过将这些逻辑封装在工厂类中来简化客户端代码。
   - 客户端代码只需要调用工厂类的一个简单方法就可以得到所需的对象,而不需要了解具体的创建过程。

4. 易于添加新产品类型
   - 当需要添加新的产品类型时,只需增加一个新的工厂类即可,而无需修改现有的客户端代码。
   - 这种开闭原则(Open-Closed Principle)的遵循使得系统更容易进行扩展。

5. 便于单元测试
   - 由于工厂模式将对象的创建过程隔离出来,因此在编写单元测试时可以方便地替换实际的产品对象为模拟对象(mock object),以减少依赖并提高测试覆盖率。

1.3 工厂模式主要解决的问题

  • 对象的创建过于复杂:如果一个对象的创建涉及到很多步骤,或者需要复杂的条件判断,那么工厂模式可以帮助将这些逻辑封装在一个地方,使得客户端代码更简洁。
  • 过度依赖具体的产品类:如果没有使用工厂模式,客户端代码可能会直接引用具体的产品类,这会导致高度的耦合。工厂模式可以将这种依赖关系转移到工厂类上,从而降低耦合度。
  • 难以扩展新类型的产品:如果系统中有很多不同种类的产品,而且可能还会继续增加,那么工厂模式可以提供一种统一的方式来管理这些产品,使得添加新类型的产品变得相对容易。
  • 需要控制对象的生命周期:在某些情况下,可能需要对对象的创建、使用和销毁进行更精细的控制。工厂模式可以通过返回不同类型的产品实例来实现这一点。

1.4 工厂模式的缺点

  1. 增加了代码复杂性:引入工厂模式后,系统中会多出一个或多个工厂类以及可能的产品类,这可能会增加代码的复杂性,尤其是在大型项目中。

  2. 违反开闭原则:尽管工厂模式有助于扩展新的产品类型,但如果需要改变已有的工厂类来支持新类型,就违背了开闭原则。这意味着每次添加新产品类型时都可能需要修改工厂类的代码。

  3. 可能增加额外的性能开销:工厂模式通常涉及一些条件判断或反射操作来决定创建哪种产品对象,这可能会带来一些额外的性能开销。

  4. 难以理解复杂的工厂结构:在某些情况下,工厂模式可能导致工厂类的结构变得复杂,特别是当存在大量产品类型和工厂类时。这可能会使代码更难理解和维护。

  5. 不适合简单的场景:如果只需要创建一种产品类型,或者创建过程非常简单,那么使用工厂模式可能会显得过于复杂,此时直接使用new关键字创建对象可能是更好的选择。

1.5 实际的应用案例

1. 数据库连接池

在数据库应用程序中,通常需要频繁地创建和销毁数据库连接。为了提高性能和减少资源消耗,可以使用连接池来管理这些连接。连接池就是一个典型的工厂模式应用。

首先定义一个DatabaseConnection接口,表示数据库连接的抽象类型。然后创建多个实现这个接口的具体类,例如MySQLConnectionPostgreSQLConnection等,它们分别代表与不同类型的数据库建立连接。

接下来创建一个DatabaseConnectionFactory接口,声明一个方法createConnection()用于创建数据库连接。针对每种数据库类型,都创建一个具体的工厂类,如MySQLConnectionFactoryPostgreSQLConnectionFactory等,它们实现了DatabaseConnectionFactory接口,并且在createConnection()方法中返回对应的数据库连接实例。

客户端代码只需要知道如何使用DatabaseConnection接口,而不必关心具体使用的数据库类型。当需要创建新的数据库连接时,只需调用相应的工厂类的createConnection()方法即可。

2. 图形用户界面(GUI)组件

在图形用户界面编程中,工厂模式也可以用来创建各种UI组件。例如,我们可以定义一个Button接口,表示按钮的抽象类型。然后为每个平台(如Windows、MacOS、Linux等)创建一个实现Button接口的具体类,比如WindowsButtonMacOSButtonLinuxButton等。

接着定义一个ButtonFactory接口,声明一个方法createButton()用于创建按钮。为每个平台创建一个具体的工厂类,如WindowsButtonFactoryMacOSButtonFactoryLinuxButtonFactory等,它们实现了ButtonFactory接口,并且在createButton()方法中返回对应的按钮实例。

客户端代码可以根据运行时环境选择合适的工厂类来创建按钮。这样可以使代码更加灵活,更容易适应不同平台的需求。

3. 文件操作

在处理文件时,可以使用工厂模式来根据文件的扩展名创建适当的文件处理器。首先定义一个FileProcessor接口,包含读取、写入等操作。然后为每种文件格式创建一个实现FileProcessor接口的具体类,如TextFileProcessorImageFileProcessorAudioFileProcessor等。

接下来定义一个FileProcessorFactory接口,声明一个方法createProcessor(filename)用于根据文件名创建文件处理器。为每种文件格式创建一个具体的工厂类,如TextFileProcessorFactoryImageFileProcessorFactoryAudioFileProcessorFactory等,它们实现了FileProcessorFactory接口,并且在createProcessor()方法中根据文件扩展名返回对应的文件处理器实例。

客户端代码只需要知道如何使用FileProcessor接口,而不必关心处理哪种类型的文件。当需要对文件进行操作时,只需调用相应的工厂类的createProcessor()方法即可。

以上是三个具体的工厂模式应用案例。实际上,任何需要动态创建对象并且有多种可能的产品类型的情况,都可以考虑使用工厂模式。您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。Hibernate 换数据库只需换方言和驱动就可以。

二、各种工厂模式的变形

工厂模式主要有以下几种形式:

1.1 简单工厂模式

  • 定义一个工厂类,该类可以根据传入的参数返回不同种类的产品实例。
  • 简单工厂模式不是Gang of Four (GoF)所定义的23种经典设计模式之一,但它是一个常用的设计模式变体,也被广泛称为静态工厂方法。

1.2 工厂方法模式

  • 在抽象基类中声明一个创建产品的方法,但不提供具体的实现,由子类来实现这个方法。
  • 每个子类根据需要创建自己的产品类型。
  • 这种模式支持添加新的产品类型时只需要增加一个新的子类即可。

1.3 抽象工厂模式

  • 提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体的类。
  • 适用于需要提供多个相关的对象系列的情况。
  • 与工厂方法模式相比,抽象工厂模式更注重的是创建一组相关或相互依赖的产品。

三、基本的代码实现

下面是一个简单的工厂模式的示例,使用 Java 语言来实现:

// 定义一个接口或抽象类
public interface Product {void use();
}// 实现具体的产品1
public class ConcreteProduct1 implements Product {@Overridepublic void use() {System.out.println("Using concrete product 1.");}
}// 实现具体的产品2
public class ConcreteProduct2 implements Product {@Overridepublic void use() {System.out.println("Using concrete product 2.");}
}// 工厂类
public class Factory {// 根据传入的参数返回不同的产品public static Product createProduct(String type) {if ("product1".equals(type)) {return new ConcreteProduct1();} else if ("product2".equals(type)) {return new ConcreteProduct2();} else {throw new IllegalArgumentException("Invalid type");}}
}// 客户端代码
public class Client {public static void main(String[] args) {Product product1 = Factory.createProduct("product1");product1.use();Product product2 = Factory.createProduct("product2");product2.use();}
}

在上面的示例中,我们定义了一个`Product`接口和两个实现了该接口的具体产品类`ConcreteProduct1`和`ConcreteProduct2`。然后我们创建了一个`Factory`类,它有一个静态方法`createProduct`,根据传入的字符串参数返回不同种类的产品实例。

客户端代码通过调用`Factory.createProduct`方法来获取需要的产品实例,并使用它们。

如果你想要 Python 的示例代码,下面是等效的实现:

# 定义一个抽象基类
from abc import ABC, abstractmethodclass Product(ABC):@abstractmethoddef use(self):pass# 实现具体的产品1
class ConcreteProduct1(Product):def use(self):print("Using concrete product 1.")# 实现具体的产品2
class ConcreteProduct2(Product):def use(self):print("Using concrete product 2.")# 工厂类
class Factory:# 根据传入的参数返回不同的产品@staticmethoddef create_product(product_type):if product_type == "product1":return ConcreteProduct1()elif product_type == "product2":return ConcreteProduct2()else:raise ValueError("Invalid type")# 客户端代码
def main():product1 = Factory.create_product("product1")product1.use()product2 = Factory.create_product("product2")product2.use()if __name__ == "__main__":main()

这段 Python 代码的功能与上述 Java 代码相同,只是语法有所不同。在这里,我们使用了 Python 的 `abc` 模块来定义一个抽象基类(抽象类),并利用 `@abstractmethod` 装饰器来标记需要子类实现的方法。其他部分的逻辑和结构基本保持一致。

PS:设计模式的梳理系列,暂时告一段落。谢谢阅读。

相关文章:

设计模式基础——工厂模式剖析(2/2)

目录 一、工厂模式 1.1 工厂模式的定义 1.2 工厂模式的设计意图 1.3 工厂模式主要解决的问题 1.4 工厂模式的缺点 1.5 实际的应用案例 1. 数据库连接池 2. 图形用户界面(GUI)组件 3. 文件操作 二、各种工厂模式的变形 1.1 简单工厂模式&#…...

spark3.x 读取hudi报错

报错信息如下: Exception in thread "main" org.apache.hudi.exception.HoodieUpsertException: Failed to upsert for commit time 20231201203145254 at org.apache.hudi.table.action.commit.BaseWriteHelper.write(BaseWriteHelper.java:64) at org.apa…...

微信小程序中block和View组件的使用区别

block和View组件都是用于布局的组件: 1. Block组件: Block组件是一个无实际显示效果的组件,它主要用于包裹一组组件,并提供了类似于div的作用。使用Block组件可以将一组组件进行分组,便于样式的管理和控制。Block组件不会在页面…...

代码混淆技术探究与工具选择

代码混淆技术探究与工具选择 引言 在软件开发中,保护程序代码的安全性是至关重要的一环。代码混淆(Obfuscated code)作为一种常见的保护手段,通过将代码转换成难以理解的形式来提升应用被逆向破解的难度。本文将介绍代码混淆的概…...

selenium 解决 id定位、class定位中,属性值带空格的解决办法

一、前置说明 selenium遇到下面这种元素&#xff1a; <th id"demo id" class"value1 value2 value3 ">1、虽然id一般不会有空格&#xff0c;但是前端错误的这种写法(如下图)&#xff0c;会造成使用id定位不到元素&#xff0c;如&#xff1a; find…...

gma 空间绘图实战(1):绘制多个子图,连接并展示局部放大区域

安装 gma&#xff1a;pip install gma 本文基于&#xff1a;gma 2.0.3&#xff0c;Python 3.10 本文用到的矢量数据为&#xff1a;CTAmap 1.12。来源于 https://www.shengshixian.com/ 。&#xff08;感谢锐多宝&#xff09; 绘图目标 参考代码 import matplotlib.pyplot as p…...

Unity中C#使用协程控制Shader材质变化

文章目录 前言一、协程是什么二、在Unity中使用协程1、我们在 Start 中测试一下协程的执行顺序2、我们实现一个点击按钮实现角色受击效果 三、协程中的动画过渡1、首先&#xff0c;在协程内实现中毒并且消散的效果2、在 OnGUI 内&#xff0c;给一个新按钮使用刚刚定义的协程 四…...

WordPress禁止显示指定类别的文章

使用wordpress禁止输出指定类别的文章可以给get_posts()函数传个数组参数&#xff0c;如下&#xff1a; <div class"widget" id"diary1"> <h3>随机呈现</h3> <ul> <?php $argsarray( numberposts>16, category>-9,-12, …...

C#里面的泛型(T),泛型类,泛型方法,泛型接口等简单解释

https://blog.csdn.net/dap769815768/article/details/81946506 只是比较简单的解释&#xff0c;在实际使用中&#xff0c;如果遇到需要深入研究的场景&#xff0c;再翻阅相关资料深入研究下。 一、泛型T 这个T在实际使用中很常见&#xff0c;比如List<T>。其实我们还…...

C语言——指针(五)

&#x1f4dd;前言&#xff1a; 上篇文章C语言——指针&#xff08;四&#xff09;更加深入的介绍了不同类型指针的特点&#xff0c;这篇文章主要想记录一下函数与指针的结合运用以及const和assert关于指针的用法&#xff1a; 1&#xff0c;函数与指针 2&#xff0c;const 3&am…...

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑气电联合需求响应的气电综合能源配网系统协调优化运行》

这个标题涉及到一个涉及气体&#xff08;天然气&#xff09;和电力的综合能源配网系统&#xff0c;并且强调了考虑气电联合需求响应的协调优化运行。让我们逐步解读&#xff1a; 气电综合能源配网系统&#xff1a; 这指的是一个结合了气体&#xff08;通常是天然气&#xff09;…...

PostgreSQL 主键和唯一键的区别

主键和唯一键的区别 主键&#xff08;Primary Key&#xff09;&#xff1a; 主键是用于唯一标识表中的每一条记录的键。主键必须是唯一的&#xff0c;不允许为空。一个表只能有一个主键。主键可以由一个或多个字段组成。主键的值在整个表中必须是唯一的&#xff0c;用于确保数据…...

删除表格中的所有绘图

Ctrl G 调出定位的对话框再点击定位条件 按Delete键&#xff0c;删除...

Linux卸载Nginx

1、停止Nginx软件 #/usr/local/nginx/sbin/nginx-sstop 或者kill进程 #ps -ef|grep nginx #kill -9 PID 2、查找根下所有名子包含nginx的文件 #sudofind/-namenginx* 3、执行命令删掉nignx安装的相关文件 # rm -rf /usr/local/sbin/nginx # rm -rf /usr/local/nginx # r…...

Qt之QGraphicsView —— 笔记1:绘制简单图元(附完整源码)

效果 相关类介绍 QGraphicsView类提供了一个小部件,用于显示QGraphicsScene的内容。QGraphicsView在可滚动视口中可视化。QGraphicsView将滚动其视口,以确保该点在视图中居中。 QGraphicsScene类 提供了一个用于管理大量二维图形项的场景。请注意,QGraphicsScene没有自己的视…...

SpringIoC原理

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…...

如何对售后服务的全流程进行精细化的管理?

——“如何对售后服务的全流程进行精细化的管理&#xff1f;” ——“售后又是一个十分复杂的过程&#xff0c;仅靠手工或者电子表格记录这些内容&#xff0c;肯定是低效率、易出错的。最好的办法是借助合适的管理工具进行精细化的过程管理。” 假设你购买了一台新的家用电器…...

SAP UI5 walkthrough step2 Bootstrap

我的理解&#xff0c;这就是一个引导指令 1.我们右键打开命令行--执行 ui5 use OpenUI5 2.执行命令&#xff1a;ui5 add sap.ui.core sap.m themelib_sap_horizon 执行完之后&#xff0c;会更新 yaml 文件 3.修改index.html <!DOCTYPE html> <html> <head&…...

Gemini:定义下一代人工智能的里程碑

Google最近发布号称世界最强的大模型"Gemini"&#xff0c;其强大多模态LLM&#xff0c;标志着AI技术的一个新时代。 Gemini作为"迄今为止最强大的AI模型"之一&#xff0c;其独特之处在于它融合了多种模式的处理能力&#xff0c;能够同时理解和生成文本、代…...

一些系统日常运维命令和语句

一、前言 记录一些日常系统运维的命令和语句 二、linux命令与语句 1、linux查看各目录使用磁盘情况 du -h /home home为目录 du -h /home 2.查看内存使用情况 free -h 3、查看进程和CPU使用情况 top top 三、数据库语句 1、统计mysql数据库表数量 SELECT COUNT(*) A…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...