创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式
本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。
由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。
一、简单工厂模式
解决问题:在调用时不想判断来实例化哪一个类或者实例化的过程过于复杂。
举个例子:我们读取配置文件,根据配置文件的后缀(json,xml,yaml)来选择不同解析器(JsonRuleConfigParser
、XmlRuleConfigParser
、YamlRuleConfigParser
),最后将文件解析为RuleConfig格式。这样具体调用的时候我们就不需要管到底实例化哪一个解析器,只需要传入需要解析的文件路径。
1、定义解析器接口
public interface IRuleConfigParser {
}
2、定义各类解析器
public class JsonRuleConfigParser implements IRuleConfigParser{
}public class XmlRuleConfigParser implements IRuleConfigParser{
}public class YamlRuleConfigParser implements IRuleConfigParser{
}
3、定义工厂类
public class RuleConfigParserFactory {public IRuleConfigParser getParserInstance(String path) {//获取文件后缀String fileExtension = getFileExtension();IRuleConfigParser parser = null;if ("json".equals(fileExtension)) {parser = new JsonRuleConfigParser();} else if ("xml".equals(fileExtension)) {parser = new XmlRuleConfigParser();} else if ("yaml".equals(fileExtension)) {parser = new YamlRuleConfigParser();}return parser;}
}
总结:简单工厂模式就是把创建对象的活单独抽出来放一个工厂类中。
优点:对象的创建和使用进行了分离,如果创建方式改了只修改工厂类就可以了。
缺点:扩展性差,加一个新的对象的时候需要修改工厂文件,增加if-else。
二、工厂方法模式
适用工厂方法优化上面例子,代码如下:
1、给每个解析器创建一个工厂类,工厂类又实现了IRuleConfigParserFactory
接口;
public interface IRuleConfigParserFactory {IRuleConfigParser createParser();
}public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new JsonRuleConfigParser();}
}public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new XmlRuleConfigParser();}
}public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new YamlRuleConfigParser();}
}
2、工厂抽象出来后,看看怎么用:
public class RuleConfigParserFactory {public IRuleConfigParser getParserInstance(String path) {//获取文件后缀String fileExtension = getFileExtension();IRuleConfigParserFactory factory= null;if ("json".equals(fileExtension)) {factory = new JsonRuleConfigParserFactory();} else if ("xml".equals(fileExtension)) {factory = new XmlRuleConfigParserFactory();} else if ("yaml".equals(fileExtension)) {factory = new YamlRuleConfigParserFactory();}return factory.createParser();}
}
看完上面的代码是不是觉得又进入了if-else
的圈子。看了王争老师的优化,优化后代码如下,通过单例可以避免if-else
,但是这种方法也有局限性,如果每次需要创建不同的新对象,下面的方法就不行了:
public class JsonRuleConfigParserFactoryMap {private static final Map<String, IRuleConfigParserFactory> cachedFactory = new HashMap<>();static {cachedFactory.put("json", new JsonRuleConfigParserFactory());cachedFactory.put("xml", new XmlRuleConfigParserFactory());cachedFactory.put("yaml", new YamlRuleConfigParserFactory());}public static IRuleConfigParserFactory getRuleConfigParserFactory(String fileExtension) {if (TextUtils.isEmpty(fileExtension)) {return null;}return cachedFactory.get(fileExtension);}
}
把每个解析器的创建又封装到了工厂类里,我个人觉得除了代码结构变复杂了,没看到工厂方法比简单工厂好在哪。我看了很多例子,大体都是这样,要么没有写最后的用法,要么僻重就轻把使用改成如下:
JsonRuleConfigParserFactory jsonParserFactory = new JsonRuleConfigParserFactory();
IRuleConfigParser jsonParser = jsonParserFactory.createParser();
所以学到这里,工厂方法的优势没有get到,最终的使用还是要if-else
判断,并且每增加一个解析器就会增加一个Factory
类。
看王争老师总结之所以会有上面的疑惑是因为:这种简单的使用场景其实并不适合使用工厂方法,因为这个例子中,工厂方法需要额外建Factory
类,并且类里就一句new
对象代码,没必要设计成独立类,所以对于这个例子,简单工厂比工厂方法更适合。
工厂方法适用场景:对象的创建比较复杂,不只是简单new
,例如还要组合其他类,做各种初始化操作,这种才比较适合工厂方法场景。
下面是刚才简单工厂获取解析器的逻辑,如果获取parser
的方式比较复杂,还需要组合其他类,做各种不同的初始化操作,这样getParserInstance
函数逻辑就会比较复杂,就需要单独拆出工厂类去创建才会更合理。
public class RuleConfigParserFactory {public IRuleConfigParser getParserInstance(String path) {//获取文件后缀String fileExtension = getFileExtension();IRuleConfigParser parser = null;if ("json".equals(fileExtension)) {parser = new JsonRuleConfigParser();} else if ("xml".equals(fileExtension)) {parser = new XmlRuleConfigParser();} else if ("yaml".equals(fileExtension)) {parser = new YamlRuleConfigParser();}return parser;}
}
三、抽象工厂
抽象工厂主要在工厂方法的基础上解决多分类问题,前面我们获取解析器是通过RuleConfig规则来区分的,如果再加一个分类,要通过系统配置规则来区分的呢?使用工厂方法模式,我们需要添加三个解析器,再加三个工厂类。扩展的时候太过繁琐,为解决这个问题,可以再抽象出一个接口。
JsonRuleConfigParser()
XmlRuleConfigParser()
YamlRuleConfigParser()
//需要增加的
JsonSystemConfigParser()
XmlSystemConfigParser()
YamlSystemConfigParser()
抽象工厂:
public interface IConfigParserFactory {IRuleConfigParser createRuleConfigParser();ISystemConfigParser createSystemConfigParser();//扩展分类方式
}public class JsonConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleConfigParser() {return new JsonRuleConfigParser();}@Overridepublic ISystemConfigParser createSystemConfigParser() {return new JsonSystemConfigParser();}
}public class XmlConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleConfigParser() {return new XmlRuleConfigParser();}@Overridepublic ISystemConfigParser createSystemConfigParser() {return new XmlSystemConfigParser();}
}public class YamConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleConfigParser() {return new YamRuleConfigParser();}@Overridepublic ISystemConfigParser createSystemConfigParser() {return new YamSystemConfigParser();}
}
按照上面抽象工厂的方式,无论添加多少分类方式,这三个工厂类就可以满足。
四:JDK中工厂使用
DateFormat
:使用的是简单工厂,只需要提供DateStyle
和TimeStyle
,可快速得到一个DateFormat
对象。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale aLocale);
//使用
public static void main(String[] args) {DateFormat format = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT);String now = format.format(new Date());System.out.println(now);}
参考文章:
简单工厂模式、工厂方法模式和抽象工厂模式有何区别?
极客时间《设计模式》(王争)
相关文章:

创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式
本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。 由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。 一、简单工厂模式 解决问题:…...

LeetCode3.无重复字符的最长子串
虽然是一道中等题,但我5分钟就写完了,而且是看完题就知道怎么写,这一看就知道双指针,一个左一个右,右指针往后移如果没有重复的长度1;如果有重复的,左指针往右移,那如何判断重复呢&a…...

鲁图中大许少辉博士八一新书《乡村振兴战略下传统村落文化旅游设计》山东省图书馆典藏
鲁图中大许少辉博士八一新书《乡村振兴战略下传统村落文化旅游设计》山东省图书馆典藏...

如何发布自己的小程序
小程序的基础内容组件 text: 文本支持长按选中的效果 <text selectable>151535313511</text> rich-text: 把HTML字符串渲染为对应的UI <rich-text nodes"<h1 stylecolor:red;>123</h1>"></rich-text> 小程序的…...

【微服务】spring 条件注解从使用到源码分析详解
目录 一、前言 二、spring 条件注解概述 2.1 条件注解Conditional介绍 2.2 Conditional扩展注解 2.2.1 Conditional扩展注解汇总 三、spring 条件注解案例演示 3.1 ConditionalOnBean 3.2 ConditionalOnMissingBean 3.2.1 使用在类上 3.2.2 使用场景补充 3.3 Condit…...

客户案例:高性能、大规模、高可靠的AIGC承载网络
客户是一家AIGC领域的公司,他们通过构建一套完整的内容生产系统,革新内容创作过程,让用户以更低成本完成内容创作。 客户网络需求汇总 RoCE的计算网络RoCE存储网络1.不少于600端口200G以太网接入端口,未来可扩容至至少1280端口1.…...

Flutter性能揭秘之RepaintBoundary
作者:xuyisheng Flutter会在屏幕上绘制Widget。如果一个Widget的内容需要更新,那就只能重绘了。尽管如此,Flutter同样会重新绘制一些Widget,而这些Widget的内容仍有部分未被改变。这可能会影响应用程序的执行性能,有时…...

29.Netty源码之服务端启动:创建EventLoopSelector流程
highlight: arduino-light 源码篇:从 Linux 出发深入剖析服务端启动流程 通过前几章课程的学习,我们已经对 Netty 的技术思想和基本原理有了初步的认识,从今天这节课开始我们将正式进入 Netty 核心源码学习的课程。希望能够通过源码解析的方式…...

Kotllin实现ArrayList的基本功能
前言 上次面试时,手写ArrayList竟然翻车,忘了里面的扩容与缩容的条件,再次实现一次,加深印象 源码讲了什么 实现了List列表和RandomAccess随机访问接口List具有增删改查功能,RandomAccess支持下标访问内部是一个扩容…...

C++的初步介绍,以及C++与C的区别
C和C的区别 C又称C plus plus,且C语言是对C语言的扩充,几乎支持所有的C语言语法;C语言:面向过程的语言(注重问题的解决方法和算法)C:面向对象的语言 (求解的方法)面向对…...

JDK 核心jar之 rt.jar
一、JDK目录展示 二、rt.jar 简介 2.1.JAR释义 在软件领域,JAR文件(Java归档,英语:Java Archive)是一种软件包文件格式,通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等&…...

el-form表单验证:只在点击保存时校验(包含select、checkbox、radio)
1、input类型 input类型 在el-input里加入:validate-event"false" <el-form-item label"活动名称" prop"name"><el-input v-model"ruleForm.name" :validate-event"false"></el-input> </el-form-i…...

Golang基本语法(上)
1. 变量与常量 Golang 中的标识符与关键字 标识符 Go语言中标识符由字母数字和_(下划线)组成,并且只能以字母和_开头。 举几个例子:abc, _, _123, a123。 关键字 关键字和保留字都不建议用作变量名: Go语言中有25个关键字。 此…...

jenkins使用
安装插件 maven publish over ssh publish over ssh 会将打包后的jar包,通过ssh推送到指定的服务器上,,在jenkins中设置,推送后脚本,实现自动部署jar包,, 装了这个插件之后,可以在项…...

多线程基础篇(包教包会)
文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)(重点&#…...

Android/Java中,各种数据类型之间的互相转换,给出各种实例,附上中文注释
目录 1.字符串(String)转整数(int): 2.整数(int)转字符串(String): 3.字符串(String)转浮点数(float)&…...

机器学习知识点总结:什么是EM(最大期望值算法)
什么是EM(最大期望值算法) 在现实生活中,苹果百分百是苹果,梨百分白是梨。 生活中还有很多事物是概率分布,比如有多少人结了婚,又有多少人有工作, 如果我们想要调查人群中吸大麻者的比例呢?敏感问题很难得…...

漏洞挖掘和安全审计的技巧与策略
文章目录 漏洞挖掘:发现隐藏的弱点1. 源代码审计:2. 黑盒测试:3. 静态分析工具: 安全审计:系统的全面评估1. 渗透测试:2. 代码审计:3. 安全策略审查: 代码示例:SQL注入漏…...

[SpringBoot3]Web服务
五、Web服务 基于浏览器的B/S结构应用十分流行。SpringBoot非常适合Web应用开发,可以使用嵌入式Tomcat、Jetty、Undertow或Netty创建一个自包含的HTTP服务器。一个SpringBoot的Web应用能够自己独立运行,不依赖需要安装的Tomcat、Jetty等。SpringBoot可以…...

构建系统自动化-autoreconf
autoreconf简介 autoreconf是一个GNU Autotools工具集中的一个命令,用于自动重新生成构建系统的配置脚本和相关文件。 Autotools是一组用于自动化构建系统的工具,包括Autoconf、Automake和Libtool。它们通常用于跨平台的软件项目,以便在不同…...

Mysql之InnoDB和MyISAM的区别
InnoDB和MyISAM是MySQL数据库中两种常见的存储引擎,它们在功能和性能方面有一些明显的区别。下面是它们之间的详细解释和说明: 底层数据 存数据的时候,MyISAM是数据和索引分开存储,分为MYD和MYI 而InnoDB是数据即索引࿰…...

Unity 之 Transform.Translate 实现局部坐标系中进行平移操作的方法
文章目录 Translate 默认使用局部坐标也可以转换成世界坐标 Translate 默认使用局部坐标 在Unity中,Transform.Translate是用于在游戏对象的局部坐标系中进行平移操作的方法。这意味着它将游戏对象沿着其自身的轴进行移动,而不是世界坐标轴。这在实现物…...

PostgreSQL Error: sorry, too many clients already
Error PG的默认最大连接数是100. 如果超过100就会报错sorry, too many clients already Find show max_connections; SELECT COUNT(*) from pg_stat_activity; SELECT * FROM pg_stat_activity;Solution 提高最大连接数 ALTER SYSTEM SET max_connections 然后重启pg查看…...

Vue2(路由)
目录 一,路由原理(hash)二,路由安装和使用(vue2)三,路由跳转四,路由的传参和取值五,嵌套路由六,路由守卫最后 一,路由原理(hash&#…...

中介者模式-协调多个对象之间的交互
在深圳租房市场,有着许多的“二房东”,房主委托他们将房子租出去,而租客想要租房的话,也是和“二房东”沟通,租房期间有任何问题,找二房东解决。对于房主来说,委托给“二房东”可太省事了&#…...

Python框架【自定义过滤器、自定义数据替换过滤器 、自定义时间过滤器、选择结构、选择练习、循环结构、循环练习、导入宏方式 】(三)
👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误…...

红黑树遍历与Redis存储
引言 在计算机科学领域,红黑树(Red-Black Tree)是一种自平衡的二叉查找树,它能在O(log n)的时间复杂度内完成插入、删除和查找操作。由于其高效性和可预测性的性能,红黑树在许多领域都得到广泛应用。本文将重点介绍红…...

前端处理图片文件的方法
在项目开发过程中,有一个需求,需要前端对上传的图片进行处理,以字符串的形式传给后端,实现效果如下: 1.上传图片的组件 在该项目中,使用了element plus组件库 <el-uploadv-model:file-list"fileL…...

「Java」《深入解析Java多线程编程利器:CompletableFuture》
《深入解析Java多线程编程利器:CompletableFuture》 一、 引言1. 对多线程编程的需求和挑战的介绍2. 介绍CompletableFuture的作用和优势 二. CompletableFuture简介1. CompletableFuture是Java中提供的一个强大的多线程编程工具2. 与传统的Thread和Runnable相比的优…...

Docker容器与虚拟化技术:容器运行时说明与比较
目录 一、理论 1.容器运行时 2.容器运行时接口 3.容器运行时层级 4.容器运行时比较 5.强隔离容器 二、问题 1.K8S为何难以实现真正的多租户 三、总结 一、理论 1.容器运行时 (1)概念 Container Runtime 是运行于 k8s 集群每个节点中ÿ…...