设计模式(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/198030.shtml

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

相关文章

FacetWP Relevanssi Integration相关性集成插件

点击阅读FacetWP Relevanssi Integration相关性集成插件原文 FacetWP Relevanssi Integration相关性集成插件是FacetWP与用于高级搜索的 Relevanssi 插件的集成显着增强了您网站的搜索功能。这个强大的工具使您的用户能够轻松找到他们寻求的特定内容,无论他们的查询…

MySQL:找回root密码

一、情景描述 我们在日常学习中,经常会忘记自己的虚拟机中MySQL的root密码。 这个时候,我们要想办法重置root密码,从而,解决root登陆问题。 二、解决办法 1、修改my.cnf配置文件并重启MySQL 通过修改配置文件,来跳…

Course2-Week2-神经网络的训练方法

Course2-Week2-神经网络的训练方法 文章目录 Course2-Week2-神经网络的训练方法1. 神经网络的编译和训练1.1 TensorFlow实现1.2 损失函数和代价函数的数学公式 2. 其他的激活函数2.1 Sigmoid激活函数的替代方案2.2 如何选择激活函数2.3 为什么需要激活函数 3. 多分类问题和Soft…

CSS实现小球边界碰撞回弹

如何通过CSS实现一个物体在屏幕中无限的边界碰撞回弹呢?我们可以使用动画效果实现 代码 我们只做一个小球,通过定位属性叠加动画的方式, 让小球在屏幕中进行运动,通过设置animation的alternate属性来设置回弹。最后,只…

智能优化算法应用:基于人工电场算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于人工电场算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于人工电场算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工电场算法4.实验参数设定5.算法结果6.参考…

golang实现函数yamlToStruct(infile,outFile)

问: golang实现函数yamlToStruct(infile,outFile),将yaml文件格式化成golang的结构体 gpt: 要实现一个将YAML文件格式化成Golang结构体的函数,你可以使用 yaml 和 reflect 包来处理。首先,你需要使用 yaml.Unmarshal 函数将YAML文件解析为一…

运维03:LAMP

黄金架构LAMP 什么是LAMP LAMP是公认的最常见,最古老的黄金web技术栈 快速部署LAMP架构 #停止nginx,并且把nginx应用卸载了 systemctl stop nginx yum remove nginx -y#关闭防火墙 iptables -F #清空防火墙规则,比如哪些请求允许进入服…

06 数仓平台MaxWell

Maxwell简介 Maxwell是由Zendesk公司开源,用 Java 编写的MySQL变更数据抓取软件,能实时监控 MySQL数据库的CRUD操作将变更数据以 json 格式发送给 Kafka等平台。 Maxwell输出数据格式 Maxwell 原理 Maxwell工作原理是实时读取MySQL数据库的二进制日志…

angular新版本未生成app.module.ts

https://angular.cn/tutorial/tour-of-heroes https://angular.cn/tutorial/tour-of-heroes/toh-pt0 ng new angular-tour-of-heroes cd angular-tour-of-heroes ng serve --open 查看目录,未生成app.module.ts 网上搜了一下 https://github.com/angular/angul…

【算法】蓝桥杯2013国C 横向打印二叉树 题解

文章目录 题目链接题目描述输入格式输出格式样例自己的样例输入自己的样例输出 思路整体思路存储二叉搜索树中序遍历并存储计算目标数的行号dfs遍历并写入数组初始化和处理输入输出初始化处理输入处理输出 完整的代码如下 结束语更新初始化的修改存储二叉搜索树的修改中序遍历和…

C语言实现梁友栋-Barsky算法

前言: (引用梁友栋-Barsky裁剪算法_梁友栋 barsky算法-CSDN博客) Cyrus和Beck用参数化方法提出了比Cohen-Sutherland更有效的算法。后来梁友栋和Barsky独立地提出了更快的参数化线段裁剪算法,也称为Liany-Barsky(LB&…

oops-framework框架 之 音频管理(六)

引擎: CocosCreator 3.8.0 环境: Mac Gitee: oops-game-kit 注: 作者dgflash的oops-framework框架QQ群: 628575875 简介 在CocosCreator中,音频的播放主要通过组件: AudioSource 来管理音乐、音效的播放、暂停和停止…

【尚硅谷】第07章:随堂复习与企业真题(面向对象-进阶)

来源:尚硅谷Java零基础全套视频教程(宋红康2023版,java入门自学必备) 基本都是宋老师发的资料里面的内容,只不过补充几个资料里没直接给出答案的问题的答案。 不想安装markdown笔记的app所以干脆在这里发一遍。 第07章:随堂复习…

k8s的部署管理以及prometheus相关监控

安装运行prometheus和grafana编辑prometheus配置文件,增加采集prometheus自身数据的采集规则grafana上导入prometheus相关图表的dashboard 前面三步主要根据https://blog.csdn.net/shnu_cdk/article/details/132182858?spm1001.2014.3001.5506 用Go编写一个prome…

C# 热键注册工具类

写在前面 介绍一个验证过的热键注册工具类,使用系统类库user32.dll中的RegisterHotkey函数来实现全局热键的注册。 代码实现 [Flags]public enum KeyModifiers{Alt 1,Control 2,Shift 4,Windows 8,NoRepeat 0x4000}public static class HotKeyHelper{[DllImp…

01、pytest:帮助你编写更好的程序

简介 ​pytest框架可以很容易地编写小型、可读的测试,并且可以扩展以支持应用程序和库的复杂功能测试。使用pytest至少需要安装Python3.7或PyPy3。PyPI包名称为pytest 一个快速的例子 content of test_sample.py def inc(x):return x1def test_ansewer():assert i…

OpenCV-Python:图像卷积操作

目录 1.图像卷积定义 2.图像卷积实现步骤 3.卷积函数 4.卷积知识考点 5.代码操作及演示 1.图像卷积定义 图像卷积是图像处理中的一种常用操作,主要用于图像的平滑、锐化、边缘检测等任务。它可以通过滑动一个卷积核(也称为滤波器)在图像…

MySQL之时间戳(DateTime和TimeStamp)

MySQL之时间戳(DateTime和TimeStamp) 文章目录: MySQL之时间戳(DateTime和TimeStamp)一、DateTime类型二、TimeStamp类型三、DateTime和TimeStamp的区别 当插入数据时,需要自动记录一个时间时候&#xff0c…

MT4下载常见问题解答:解决安装过程中的技术难题

MetaTrader 4(简称MT4)是全球广泛使用的外汇交易平台,它以强大的功能、用户友好的界面和高度定制性获得了众多金融交易者的青睐。然而,对于初次接触MT4的用户来说,下载和安装MT4软件可能会遇到一些技术难题。本文作为一位资深金融技术专家的分…

人工智能_机器学习059_非线性核函数_poly核函数_rbf核函数_以及linear核函数效果对比---人工智能工作笔记0099

人工智能_机器学习059_非线性核函数介绍---人工智能工作笔记0099 那么我们应该如何调整这个SVC的参数,也就是我们应该使用哪种核函数,比较合适呢?这取决于我们的数据,适合使用哪个核函数,正好我们有 提供的score = accuracy_score(y_test,y_pred) 这样的评分函数,我们可以根据…