设计模式基础——工厂模式剖析(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:设计模式的梳理系列,暂时告一段落。谢谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/198195.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开启gzip优化页面加载速度

生成gizp的打包资源,可以优化页面加载速度 打包的时候开启gzip可以很大程度减少包的大小,页面大小可以变为原来的30%甚至更小,非常适合线上部署, 但还记得需要服务端支持 1、前端配置compression-webpack-plugin 先安装:npm install compres…

windows运行orb-slam3遇到的问题

windows版代码地址:https://github.com/melhashash/orbslam3-windows 编译完成,出现初始化不成功的现象。 问题一: 相机参数中没有相机类型,导致畸变参数初始化失败。 GrabImageRGBD中frame对象实例化时,缺少相机参数…

【Windows】永久屏蔽系统更新

永久关闭电脑更新服务 操作思路: 第一步 winR 输入 services.msc 回车 进入服务管理窗口第二步 进入窗口后 找到 w 开头的文件夹 并找到Windows Update 双击打开 Windows Update 将启动类型(E) 改为禁用 上方的 “常规” “登录” “恢…

EISeg 交互式分割标注软件安装

EISeg(Efficient Interactive Segmentation)是以RITM及EdgeFlow算法为基础,基于飞桨开发的一个高效智能的交互式分割标注软件。涵盖了通用、人像、遥感、医疗、工业质检等不同方向的高质量交互式分割模型,方便开发者快速实现语义及实例标签的标注&#x…

SNP推出新Glue软件Saas版本,助力云数据集成

最新Glue版本可作为软件即服务(SaaS)应用程序使用SAP数据和非SAP数据源之间的云原生集成大大简化了客户的企业数据集成SNP Glue通过应对AI和大数据计划中的关键挑战来增强云数据集成的价值 德国,海德堡 —— 2023年11月29日,作为SAP环境中数字化转型、自…

Vue学习笔记-<router-link>的replace的属性

router-link的replace属性 作用:控制路由跳转时操作浏览器历史记录的模式 浏览器的历史记录有两种写入方式:push和replace,其中push是追加历史记录(将浏览的url请求入栈),replace则是替换当前记录。路由跳…

第二十五章 控制到 XML 模式的映射 - 将文字属性映射到 XML 模式

文章目录 第二十五章 控制到 XML 模式的映射 - 将文字属性映射到 XML 模式将文字属性映射到 XML 模式IRIS 数据类型类的默认 XSD 类型 第二十五章 控制到 XML 模式的映射 - 将文字属性映射到 XML 模式 将文字属性映射到 XML 模式 本节讨论如何将文字(非集合&#…

如何计算 ChatGPT 的 Tokens 数量?

一、基本介绍 随着人工智能大模型技术的迅速发展,一种创新的计费模式正在逐渐普及,即以“令牌”(Token)作为衡量使用成本的单位。那么,究竟什么是Token呢? Token 是一种将自然语言文本转化为计算机可以理…

容器重启后,Conda文件完整保存(虚拟环境、库包),如何重新安装conda并迁移之前的虚拟环境

Vim安装 容器重启后默认是vi,升级vim,执行命令 apt install -y vim安装 Anaconda 1. 下载Anaconda 其他版本请查看Anaconda官方库 wget https://mirrors.bfsu.edu.cn/anaconda/archive/Anaconda3-2023.03-1-Linux-x86_64.sh --no-check-certificate…

【DBeaver】驱动添加-Hive和星环

驱动 Hive驱动 hive驱动可以直接去官网下载官网地址,填一下个人信息。 如果想直接下载可以去我上次的资源下地址,需要用zip解压。 星环驱动 星环驱动是我第一次接触,是国产的基于开源Hive驱动自研的产品,我看到官网上有很多类…

[leetcode ~二叉树] 模版

文章目录 1. 左叶子之和2. 翻转二叉树 E 1. 左叶子之和 :::details 给定二叉树的根节点 root ,返回所有左叶子之和。 示例 1: 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15&…

跨浏览器测试:如何确保你的应用在各种浏览器上都能正常运行

在当今的互联网时代,浏览器已成为我们获取信息、与他人交流、工作和娱乐的主要工具。然而,不同的浏览器、不同的版本和不同的操作系统可能会对你的应用造成不同的影响,可能使其表现出各种不同的行为和问题。为了确保你的应用能在各种浏览器环…

GaussDB数据库SQL系列-序列的使用

目录 一、前言 二、GaussDB数据库中的序列 1、语法(CREATE SEQUENCE) 2、注意事项 三、GaussDB数据库中的示例 1、示例一:创建普通序列 2、示例二:创建与表关联的序列 四、小结 一、前言 在数据库管理中,序列(SEQUENCE&a…

Nginx的使用

Nginx的使用 一、配置前端项目访问二、配置SSL证书1、正常配置2、配置报错 三、配置域名反向代理1、简单代理2、带参数代理3、指定后缀域名跳转4、访问反向代理域名的静态资源5、配置静态资源访问(1)、将域名配置到小程序,获得TXT文件&#x…

电池出现零电压和不能充放电的原因

1.单体电池出现零电压或低电压的可能原因是什么? 01)电池外部短路或过充、反充(强制过放); 02)电池受高倍率大电流连续过充,导致电池极芯膨胀,正负极直接接触短路等…

GB/T 29734.1-2013 铝木复合门窗检测

铝木复合门窗是指采用铝合金型材与木型材通过连接卡件或螺钉等连接方式制作的框、扇构件的门窗。 GB/T 29734.1-2013 铝木复合门窗检测项目 测试项目 测试标准 外观质量 GB/T 29734.1 尺寸 GB/T 29734.1 装配质量 GB/T 29734.1 抗风压性能 GB/T 7106 水密性 GB/T 7…

leetcode:对称二叉树

题目描述 题目链接:101. 对称二叉树 - 力扣(LeetCode) 题目分析 题目中说至少存在一个节点,所以我们只需要对比左右子树 写一个子函数对比左右子树:用递归的思路,左子树的左子树和右子树的右子树对比&…

2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-B

2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-B 目录 2024 年甘肃省职业院校技能大赛中职组 电子与信息类“网络安全”赛项竞赛样题-B 需要环境或者解析可以私信 (二)A 模块基础设施设置/安全加固(200 分&…

使用Python Flask搭建Web问答应用程序并发布到公网远程访问

使用Python Flask搭建web问答应用程序框架,并发布到公网上访问 文章目录 使用Python Flask搭建web问答应用程序框架,并发布到公网上访问前言1. 安装部署Flask并制作SayHello问答界面2. 安装Cpolar内网穿透3. 配置Flask的问答界面公网访问地址4. 公网远程…

人员备岗与能力备份

去成为那种具备能力备份的人,不简单是人员备岗。 11月面临多名人员请假,人员请假过程中不停的问自己一个问题?是否做好了备份。 1. 人员备份 当一个人A离开工作岗位,需要找B继续接上去推广系统。 实际情况是抽调B过来&#xf…