当前位置: 首页 > 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…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...