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

创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式

本文使用了王争老师设计模式课程中的例子,写的很清晰,而且中间穿插了代码优化。

由于设计模式就是解决问题的一种思路,所以每个设计模式会从问题出发,这样比较好理解设计模式出现的意义。

一、简单工厂模式

解决问题:在调用时不想判断来实例化哪一个类或者实例化的过程过于复杂。

举个例子:我们读取配置文件,根据配置文件的后缀(json,xml,yaml)来选择不同解析器(JsonRuleConfigParserXmlRuleConfigParserYamlRuleConfigParser),最后将文件解析为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:使用的是简单工厂,只需要提供DateStyleTimeStyle,可快速得到一个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);}

参考文章:
简单工厂模式、工厂方法模式和抽象工厂模式有何区别?
极客时间《设计模式》(王争)

相关文章:

创建型(一) - 简单工厂模式、工厂方法模式和抽象工厂模式

本文使用了王争老师设计模式课程中的例子&#xff0c;写的很清晰&#xff0c;而且中间穿插了代码优化。 由于设计模式就是解决问题的一种思路&#xff0c;所以每个设计模式会从问题出发&#xff0c;这样比较好理解设计模式出现的意义。 一、简单工厂模式 解决问题&#xff1a…...

LeetCode3.无重复字符的最长子串

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

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

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

如何发布自己的小程序

小程序的基础内容组件 text&#xff1a; 文本支持长按选中的效果 <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领域的公司&#xff0c;他们通过构建一套完整的内容生产系统&#xff0c;革新内容创作过程&#xff0c;让用户以更低成本完成内容创作。 客户网络需求汇总 RoCE的计算网络RoCE存储网络1.不少于600端口200G以太网接入端口&#xff0c;未来可扩容至至少1280端口1.…...

Flutter性能揭秘之RepaintBoundary

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

29.Netty源码之服务端启动:创建EventLoopSelector流程

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

Kotllin实现ArrayList的基本功能

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

C++的初步介绍,以及C++与C的区别

C和C的区别 C又称C plus plus&#xff0c;且C语言是对C语言的扩充&#xff0c;几乎支持所有的C语言语法&#xff1b;C语言&#xff1a;面向过程的语言&#xff08;注重问题的解决方法和算法&#xff09;C&#xff1a;面向对象的语言 &#xff08;求解的方法&#xff09;面向对…...

JDK 核心jar之 rt.jar

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

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语言中标识符由字母数字和_(下划线&#xff09;组成&#xff0c;并且只能以字母和_开头。 举几个例子&#xff1a;abc, _, _123, a123。 关键字 关键字和保留字都不建议用作变量名&#xff1a; Go语言中有25个关键字。 此…...

jenkins使用

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

多线程基础篇(包教包会)

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

Android/Java中,各种数据类型之间的互相转换,给出各种实例,附上中文注释

目录 1.字符串&#xff08;String&#xff09;转整数&#xff08;int&#xff09;&#xff1a; 2.整数&#xff08;int&#xff09;转字符串&#xff08;String&#xff09;&#xff1a; 3.字符串&#xff08;String&#xff09;转浮点数&#xff08;float&#xff09;&…...

机器学习知识点总结:什么是EM(最大期望值算法)

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

漏洞挖掘和安全审计的技巧与策略

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

[SpringBoot3]Web服务

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

构建系统自动化-autoreconf

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

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

超短脉冲激光自聚焦效应

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

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...