创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式
本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。
由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。
一、简单工厂模式
解决问题:在调用时不想判断来实例化哪一个类或者实例化的过程过于复杂。
举个例子:我们读取配置文件,根据配置文件的后缀(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。它们通常用于跨平台的软件项目,以便在不同…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...