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

重修设计模式-创建型-工厂模式

重修设计模式-创建型-工厂模式

一、概述

工厂模式(Factory Pattern)是设计模式中非常基础且常用的一种模式,主要目的是通过封装对象的创建过程,从而实现代码的解耦和灵活性的提升。

工厂模式的核心思想

  • 封装对象的创建:将对象的创建过程封装起来,使得客户端不必直接创建对象,而是通过工厂类来间接创建对象。
  • 解耦:将对象的创建与使用分离,使得客户端只需要知道如何使用对象,而不需要知道如何创建对象。
  • 提高扩展性:当需要增加新的产品类时,只需要增加一个具体的工厂类或产品类,而不需要修改原有代码,符合开闭原则(对扩展开放,对修改关闭)。

工厂模式又细分为三种类型,简单工厂工厂方法抽象工厂,其中工厂方法和抽象工厂被收录在23种设计模式中,而简单工厂被看做是工厂方法的一种特例。其实这三种工厂模式的原理和实现都非常简单,重点在于搞清应用场景,什么时候可以考虑使用工厂模式,又该用这三种类型的哪一种呢?

举个例子:

应用需要根据传入的路径去解析文件并显示,但文件可能有json、xml或yaml等格式,需要根据后缀名来创建解析实现类。

需求很简单,可以直接翻译成代码:

fun loadConfig(path: String): Config? {var parser : IRuleConfigParser? = nullif (path.endsWith("json", true)) {parser = JsonRuleParser()} else if (path.endsWith("xml", true)) {parser = XmlRuleParser()} else if (path.endsWith("yaml", true)) {parser = YamlRuleParser()}val config = parser?.parser(path)return config
}

但“根据文件后缀名创建解析器”的逻辑是相对独立的,可以将其抽成一个方法来复用,但其实还可以将这部分逻辑抽成一个类,让这个类来负责对象的创建,从而更符合单一职责原则,让代码更清晰,而抽出的类就是简单工厂模式了。

二、简单工厂(Simple Factory)

又称静态工厂方法模式(Static Factory Method Pattern),简单工厂模式不是一种设计模式,而更像是一种编程习惯。抽出的工厂类根据传入的参数,决定创建出哪一种类的实例。

下面用简单工厂封装一下对象创建的代码:

//简单工厂
class SimpleFactory {companion object {@JvmStaticfun create(path: String): IRuleConfigParser? {return if (path.endsWith("json", true)) {JsonRuleParser()} else if (path.endsWith("xml", true)) {XmlRuleParser()} else if (path.endsWith("yaml", true)) {YamlRuleParser()} else null}}
}

调用处:

fun loadConfig(path: String): Config? {val parser = SimpleFactory.create(path)val config = parser?.parser(path)return config
}

可以看到调用处代码非常简洁,创建代码都被封装到工厂类中了,开发中只要维护工厂代码即可。其实该例子创建的对象是可以复用的,那么还可以在工厂类中通过 map 缓存创建的对象,不必每次都创建新对象。同时 map 还可以取代 if-else 形式的代码:

//带缓存的简单工厂
class SimpleFactory {companion object {@JvmStaticprivate val cacheMap: HashMap<String, IRuleConfigParser> = hashMapOf("json" to JsonRuleParser(),"xml" to XmlRuleParser(),"yaml" to YamlRuleParser(),)@JvmStaticfun create(path: String): IRuleConfigParser? {val extension = getFileExtension(path)return cacheMap[extension]}@JvmStaticfun getFileExtension(filePath: String): String {//...解析文件名获取扩展名,比如rule.json,返回jsonreturn filePath.substringAfterLast('.', "")}}
}

简单工厂模式的问题是,如果需要扩展新的产品类,就需要修改工厂类的代码,违反了开闭原则,所以只适合改动不频繁的简单场景(设计原则没必要严格遵守,根据场景灵活使用才是目的)

这里的解析器创建只是简单的 new 了一个对象,如果解析器的创建流程变得复杂,那 create() 方法中的代码也会随之变得冗长,这种情况下可以把解析器复杂的创建逻辑进一步封装到各个工厂类中,这就成了工厂方法模式。

三、工厂方法(Factory Method)

工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法让类的实例化延迟到子类中进行。这种方式的好处是,新增产品类时,只需要增加一个具体的产品类和对应的具体工厂类,而不需要修改原有的工厂接口和工厂类。

下面使用工厂方法封装解析器创建流程:

//封装每个解析器的创建过程
interface IRuleConfigParserFactory {fun create(): IRuleConfigParser
}//Json解析器创建
class JsonParserFactory : IRuleConfigParserFactory {override fun create(): IRuleConfigParser {val parser = JsonRuleParser()//parser.xxx = xxx//...return parser}
}//Xml解析器创建
class XmlParserFactory: IRuleConfigParserFactory {override fun create(): IRuleConfigParser = XmlRuleParser()
}//Yaml解析器创建
class YamlParserFactory: IRuleConfigParserFactory {override fun create(): IRuleConfigParser = YamlRuleParser()
}//这里其实还是简单工厂
class MethodFactory {companion object {@JvmStaticfun create(path: String): IRuleConfigParser? {var parserFactory: IRuleConfigParserFactory? = nullif (path.endsWith("json", true)) {parserFactory = JsonParserFactory()} else if (path.endsWith("xml", true)) {parserFactory = XmlParserFactory()} else if (path.endsWith("yaml", true)) {parserFactory = YamlParserFactory()}return parserFactory?.create()}}
}

外部调用:

fun loadConfig(path: String): Config? {val parser = MethodFactory.create(path)val config = parser?.parser(path)return config
}

可以看到复杂的解析器创建逻辑被隔离到具体的工厂创建类中了,又用简单工厂简化了调用代码。可以发现,简单工厂侧重对分支代码的封装,而工厂方法更侧重隔离对象创建的复杂代码,两者还可以组合使用。

工厂方法缺点是,如果工厂不只是生产一种类型的对象,或者说创建的对象会根据多个维度分类,那工厂方法的实现类将会非常多,从而难以维护。如上面的例子,解析器除了根据文件格式分类,还增加了文件大小的维度,这时候需要实现的类就有 3 x 2 = 6个:

SmallJsonParserFactory
SmallXmlParserFactory
SmallYamlParserFactory
BigJsonParserFactory
BigXmlParserFactory
BigYamParserFactory

可见创建对象维度的增加会导致工厂类爆增,针对上面场景,就可以使用抽象工厂,让一个工厂可以生产不同类型的对象。

四、抽象工厂(Abstract Factory)

抽象工厂模式使用抽象的接口来创建一系列相关或相互依赖的对象,而无需指定它们具体的类。这种方式主要用于创建一组相互依赖或关联的对象,而不需要指定它们具体的类。

以上面新增文件大小维度举例,抽象出代码如下:

//抽象工厂
interface IConfigParserFactory {fun createJsonFactory(): IRuleConfigParserfun createXmlFactory(): IRuleConfigParserfun createYamlFactory(): IRuleConfigParser
}//大文件解析器
class ConfigBigParser: IConfigParserFactory {override fun createJsonFactory(): IRuleConfigParser {//再这里创建不同维度的产品类return JsonRuleParser()}override fun createXmlFactory(): IRuleConfigParser {return XmlRuleParser()}override fun createYamlFactory(): IRuleConfigParser {return YamlRuleParser()}
}//小文件解析器
class ConfigSmallParser: IConfigParserFactory {override fun createJsonFactory(): IRuleConfigParser {//..省略return JsonRuleParser()}override fun createXmlFactory(): IRuleConfigParser {//..省略return XmlRuleParser()}override fun createYamlFactory(): IRuleConfigParser {//..省略return YamlRuleParser()}
}

调用处代码:

fun loadConfig3(path: String): Config? {//可根据具体业务,通过抽象工厂创建不同的产品类,无须关心实现类val parser1 = ConfigBigParser()val parser2 = ConfigSmallParser()parser1.createJsonFactory()parser1.createXmlFactory()parser1.createYamlFactory()parser2.createJsonFactory()parser2.createXmlFactory()parser2.createYamlFactory()
}

抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

总结

工厂模式是将对象创建的过程封装,创建对象的复杂代码被隔离到每个工厂内部消化,从而实现对象的创建与使用分离。不同工厂模式适用于不同的场景,选择哪种模式取决于具体的需求和场景。

相关文章:

重修设计模式-创建型-工厂模式

重修设计模式-创建型-工厂模式 一、概述 工厂模式&#xff08;Factory Pattern&#xff09;是设计模式中非常基础且常用的一种模式&#xff0c;主要目的是通过封装对象的创建过程&#xff0c;从而实现代码的解耦和灵活性的提升。 工厂模式的核心思想 封装对象的创建&#x…...

使用Cskin时候 遇到按钮有默认阴影问题解决

使用Cskin时候 遇到按钮有默认阴影 设置 DrawType 属性就可以了...

121.rk3399 uboot(2017.09) 源码分析1(2024-09-05)

参考源码 : uboot&#xff08;2017.09&#xff09; 硬件平台&#xff1a;rk3399 辅助工具&#xff1a;linux虚拟机&#xff0c;sourceinsight4&#xff0c;文件浏览器&#xff08;可以使用samba访问&#xff09;&#xff0c;ultraeidt(查看bin文件比较方便) 说明&#xff1a…...

【图论】虚树 - 模板总结

适用于解决一棵树中只需要用到少部分点的时候&#xff0c;将需要用到的点提出来单独建一棵树 /********************* 虚树 *********************/ struct edge {int to, next;int val; };struct Virtual_Tree {int n; // 点数int dfn[N]; // dfs序int dep[N]; // 深度int fa…...

[C#学习笔记]注释

官方文档&#xff1a;Documentation comments - C# language specification | Microsoft Learn 一、常用标记总结 1.1 将文本设置为代码风格的字体&#xff1a;<c> 1.2 源代码或程序输出:<code> 1.3 异常指示:<exception> 1.4 段落 <para> 1.5 换行&…...

c# checkbox的text文字放到右边

checkbox的text文字放到右边 实现方法如下图 特此记录 anlog 2024年9月2日...

【node.js】基础之修改文件

node.js 基础(一) node.js是什么&#xff1f; 上面这句话的意思就是&#xff1a;Node.js 是一个开源的&#xff0c;跨平台的javascript运行环境。通俗的说就是一个应用程序或者说是一个软件&#xff0c;可以运行javascript。 Node.js的作用&#xff1a; 开发服务器应用。 将数…...

Notepad++回车不自动补全

问题 使用Notepad时&#xff0c;按回车经常自动补全&#xff0c;但我们希望回车进行换行&#xff0c;而不是自动补全&#xff0c;而且自动补全使用Tab进行补全足够了。下文介绍设置方法。 设置方法 打开Notepad&#xff0c;进入设置 - 首选项 - 自动完成&#xff0c;在插入选…...

CSS线性渐变拼接,一个完整的渐变容器(div),要拆分成多个渐变容器(div),并且保持渐变效果一致

1 需求 一个有渐变背景的div&#xff0c;需要替换成多个渐变背景div拼接&#xff0c;渐变效果需要保持一致&#xff08;不通过一个大的div渐变&#xff0c;其他子的div绝对定位其上并且背景透明来解决&#xff09; 2 分析 主要工作&#xff1a; 计算完整div背景线性渐变时的…...

【60天备战软考高级系统架构设计师——第十天:软件设计与架构综合练习】

经过前十天的学习&#xff0c;我们已经了解了软件工程生命周期模型、需求分析与管理方法&#xff0c;以及软件设计与架构的核心内容。为了巩固这些知识点&#xff0c;今天我们将进行一个综合练习。 前十天学习内容回顾 第1-3天&#xff1a;软件工程概述 学习了软件生命周期模…...

2024.8.15(python管理mysql、Mycat实现读写分离)

一、python管理mysql 1、搭建主mysql [rootmysql57 ~]# tar -xf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz [rootmysql57 ~]# cp -r mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql [rootmysql57 ~]# rm -rf /etc/my.cnf [rootmysql57 ~]# mkdir /usr/local/mysql…...

CMU 10423 Generative AI:lec2

文章目录 1 概述2 部分摘录2.1 噪声信道模型&#xff08;Noisy Channel Models&#xff09;主要内容&#xff1a;公式解释&#xff1a;应用举例&#xff1a; 2.2 n-Gram模型1. 什么是n-Gram模型2. 早期的n-Gram模型3. Google n-Gram项目4. 模型规模与训练数据5. n-Gram模型的局…...

恋爱相亲交友系统源码原生源码可二次开发APP 小程序 H5,web全适配

直播互动&#xff1a;平台设有专门的直播间&#xff0c;允许房间主人与其他异性用户通过视频连线的方式进行一对一互动。语音视频交流&#xff1a;异性用户可以发起语音或视频通话&#xff0c;以增进了解和交流。群组聊天&#xff1a;用户能够创建群聊&#xff0c;邀请自己关注…...

OceanBase 4.x 存储引擎解析:如何让历史库场景成本降低50%+

据国际数据公司&#xff08;IDC&#xff09;的报告显示&#xff0c;预计到2025年&#xff0c;全球范围内每天将产生高达180ZB的庞大数据量&#xff0c;这一趋势预示着企业将面临着更加严峻的海量数据处理挑战。随着数据日渐庞大&#xff0c;一些存储系统会出现诸如存储空间扩展…...

js 如何写构造函数 ,构造函数和普通函数有什么区别

在 JavaScript 中&#xff0c;构造函数是一种特殊的函数&#xff0c;用于初始化一个新创建的对象。构造函数通常用来创建具有相似属性和方法的对象实例。构造函数的主要特点是在调用时使用 new 关键字&#xff0c;这样就会创建一个新对象&#xff0c;并将其原型设置为构造函数的…...

MySQL-进阶篇-锁(全局锁、表级锁、行级锁)

文章目录 1. 锁概述2. 全局锁2.1 介绍2.2 数据备份2.3 使用全局锁造成的问题 3. 表级锁3.1 表锁3.1.1 语法3.1.2 读锁3.1.3 写锁3.1.4 读锁和写锁的区别 3.2 元数据锁&#xff08;Meta Data Lock&#xff0c;MDL&#xff09;3.3 意向锁3.3.1 案例引入3.3.2 意向锁的分类 4. 行级…...

c++懒汉式单例模式(Singleton)多种实现方式及最优比较

前言 关于C懒汉式单例模式的写法&#xff0c;大家都很熟悉。早期的设计模式中有代码示例。比如&#xff1a; class Singleton {private: static Singleton *instance;public: static Singleton *getInstance() {if (NULL instance)instance new Singleton();return instanc…...

Gartner《2024中国安全技术成熟度曲线》AI安全助手代表性产品:开发者安全助手D10

海云安关注到&#xff0c;近日&#xff0c;国际权威研究机构Gartner发布了《2024中国安全技术成熟度曲线》(Hype Cycle for Security in China,2024)报告。 在此次报告中&#xff0c;安全技术成熟度曲线将安全周期划分为技术萌芽期&#xff08;Innovation Trigger&#xff09;…...

奇安信椒图--服务器安全管理系统(云锁)

奇安信椒图–服务器安全管理系统&#xff08;云锁&#xff09; 椒图 奇安信服务器安全管理系统是一款符合Gartner定义的CWPP&#xff08;云工作负载保护平台&#xff09;标准、EDR&#xff08;终端检测与响应&#xff09;、EPP终端保护平台&#xff08;终端保护平台&#xff…...

pointer-events,添加水印的一个小小点

场景&#xff1a;平平无奇一个水印图&#xff0c;这类功能实现&#xff1a;就是覆盖在整个可视div后&#xff0c;又加了一个div&#xff08;使用定位canvas画一个水印图充当背景&#xff09;&#xff0c;可时我好奇的是&#xff0c;我使用控制台&#xff0c;选择对应的元素时&a…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...