设计模式(创建型模式)工厂模式

目录

    • 一、简介
      • 1.1、接口定义
      • 1.2、调用
    • 二、简单工厂模式
      • 2.1、解析工厂
      • 2.2、调用
    • 三、工厂方法模式
      • 3.1、解析器接口定义
      • 3.2、解析工厂接口定义
      • 3.3、解析器工厂的工厂
      • 3.4、调用
    • 四、抽象工厂模式
      • 4.1、内容解析器
      • 4.2、工厂类
    • 三、优点与缺点

一、简介

  工厂模式我将它分为三类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

  抽象工厂用得比较少,我主要讲解下简单工厂模式和工厂方法模式,假设我们根据配置文件的扩展名(xmlymlproperties)获取到不同的解析器XmlRuleConfigParserYmlRuleConfigParserPropertiesRuleConfigParser,从而解析得到结果(RuleConfig),相信大家很简单的就写出了如下的代码:

1.1、接口定义

public class RuleConfig {// 属性忽略
}public interface IRuleConfigParser {RuleConfig parse(String configText);
}public class XmlRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 XML 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}public class YmlRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 YML 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}public class PropertiesRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 Properties 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}

  这里我们定义了一个解析器接口,并且有三个解析都实现了该接口,最终返回解析的结果。

1.2、调用

RuleConfigSource.java

public class RuleConfigSource {public RuleConfig loadConfig() {String configFilePath = "config.xml"; // 假设配置文件路径String fileType = getFileExtension(configFilePath); // 获取文件类型(xml、yml、properties)IRuleConfigParser parser=null;if ("xml".equalsIgnoreCase(fileType)) {parser= new XmlRuleConfigParser();} else if ("yml".equalsIgnoreCase(fileType)) {parser= new YmlRuleConfigParser();} else if ("properties".equalsIgnoreCase(fileType)) {parser= new PropertiesRuleConfigParser();}else {throw new IllegalArgumentException("Unknown file type: " + fileType);}// 解析配置文件RuleConfig ruleConfig = parser.parse(configFilePath);// 返回解析后的 ruleConfig 对象return ruleConfig;}private String getFileExtension(String filePath) {if (filePath == null || filePath.isEmpty()) {throw new IllegalArgumentException("File path is invalid");}int lastDotIndex = filePath.lastIndexOf('.');if (lastDotIndex == -1) {throw new IllegalArgumentException("File path does not contain a valid file extension");}return filePath.substring(lastDotIndex + 1);}}

  我敢肯定的说,绝大部分的小伙伴都是这么写的,完全没毛病,我以前也是这么写过,不过我们今天说的是设计模式,讲的是一个接近规范的方法,而不是说代码可读性什么的。那就看看使用简单工厂模式是什么样的。

二、简单工厂模式

  简单工厂模式(Simple Factory Pattern)是通过一个工厂类来创建不同类型的对象,客户端通过工厂类的静态方法来获取所需的对象。在简单工厂模式中,工厂类负责所有产品的创建。
  首先是要理解为啥要改,一切的依据都是从七大设计原则来探讨设计模式,如果不结合实际业务,确实有点不合适。在我们使用的RuleConfigSource并没有满足单一职责原则,它并不需要知道怎么去获取解析器的详细信息;也不符合开闭原则,所有的逻辑都在这里,修改不便。在接口定义不变的前提下,改造如下:

2.1、解析工厂

RuleConfigParserFactory.java

import java.util.HashMap;
import java.util.Map;public class RuleConfigParserFactory {private static final Map<String, RuleConfigParser> PARSER_MAP = new HashMap<>();static {PARSER_MAP.put("xml", new XmlRuleConfigParser());PARSER_MAP.put("yml", new YmlRuleConfigParser());PARSER_MAP.put("properties", new PropertiesRuleConfigParser());}public static RuleConfigParser createParser(String fileExtension) {if (PARSER_MAP.containsKey(fileExtension)) {return PARSER_MAP.get(fileExtension);}return null;}}

2.2、调用

public class RuleConfigSource {public RuleConfig loadConfig() {String configFilePath = "config.xml"; // 假设配置文件路径// 获取文件后缀(可以放到工具类)String fileExtension = getFileExtension(configFilePath);// 获取解析器RuleConfigParser parser = RuleConfigParserFactory.createParser(fileExtension);if (parser==null){throw new IllegalArgumentException("Unknown file type: " + fileExtension);}// 解析配置文件RuleConfig ruleConfig = parser.parse(configFilePath);// 返回解析后的 ruleConfig 对象return ruleConfig;}private static String getFileExtension(String filePath) {// 从文件路径中提取文件类型(后缀)if (filePath == null || filePath.isEmpty()) {throw new IllegalArgumentException("File path is invalid");}int lastDotIndex = filePath.lastIndexOf('.');if (lastDotIndex == -1) {throw new IllegalArgumentException("File path does not contain a valid file extension");}return filePath.substring(lastDotIndex + 1);}}

  这里的RuleConfigSource就不会涉及到具体解析器的构建和解析,并且增加一个也与它没关系,只需要改动RuleConfigParserFactory,有些人说,这也违反了开闭原则?实际上如果不是很频繁的添加新的解析器,只是偶尔修改RuleConfigParserFactory的代码,即便不符合开闭原则,也是可以被接受的。并且这里通过静态代码块,节省了内存和对象创建的开销。

三、工厂方法模式

  工厂方法模式(Factory Method Pattern)则是将对象的创建推迟到具体工厂类中,定义一个抽象的工厂接口,每个具体工厂类都实现这个接口,负责创建特定的对象。每个具体工厂类只负责创建单一类型的对象。
  还是用之前的示例来改造成工厂方法模式,理论上工厂方法模式比简单工厂模式更加符合开闭原则。

3.1、解析器接口定义

public class RuleConfig {// 属性忽略
}public interface IRuleConfigParser {RuleConfig parse(String configText);
}public class XmlRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 XML 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}public class YmlRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 YML 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}public class PropertiesRuleConfigParser implements IRuleConfigParser {@Overridepublic RuleConfig parse(String configText) {// 实现 Properties 配置解析逻辑// 返回 RuleConfig 对象return new RuleConfig(); // 假设这里返回 RuleConfig 对象}
}

3.2、解析工厂接口定义

  我们需要给每个解析器都增加一个工厂类。

public interface IRuleConfigParserFactory {IRuleConfigParser createParser();
}public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new XmlRuleConfigParser();}
}public class YmlRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new YmlRuleConfigParser();}
}public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {@Overridepublic IRuleConfigParser createParser() {return new PropertiesRuleConfigParser();}
}

3.3、解析器工厂的工厂

  然后管理这些工程类,也就是建立一个工厂的工厂。

import java.util.HashMap;
import java.util.Map;public class RuleConfigParserFactoryMap {private static final Map<String, IRuleConfigParserFactory> PARSER_MAP = new HashMap<>();static {PARSER_MAP.put("xml", new XmlRuleConfigParserFactory());PARSER_MAP.put("yml", new YmlRuleConfigParserFactory());PARSER_MAP.put("properties", new PropertiesRuleConfigParserFactory());}public static IRuleConfigParserFactory getParserFactory(String fileExtension) {// 获取解析工厂if (PARSER_MAP.containsKey(fileExtension)) {return PARSER_MAP.get(fileExtension);}return null;}}

3.4、调用

public class RuleConfigSource {public RuleConfig loadConfig() {String configFilePath = "config.xml"; // 假设配置文件路径String fileExtension = getFileExtension(configFilePath);// 获取文件类型(xml、yml、properties)// 获取解析器IRuleConfigParserFactory parserFactory = RuleConfigParserFactoryMap.getParserFactory(fileExtension);if (parserFactory == null) {throw new IllegalArgumentException("未找到解析工厂");}IRuleConfigParser parser = parserFactory.createParser();// 返回解析后的 ruleConfig 对象return parser.parse(configFilePath);}private static String getFileExtension(String filePath) {// 从文件路径中提取文件类型(后缀)// 这里假设实现获取文件类型的逻辑if (filePath == null || filePath.isEmpty()) {throw new IllegalArgumentException("File path is invalid");}int lastDotIndex = filePath.lastIndexOf('.');if (lastDotIndex == -1) {throw new IllegalArgumentException("File path does not contain a valid file extension");}return filePath.substring(lastDotIndex + 1);}}

  当我们需要新的解析器时,只需要定义新的解析器对应的类和工厂类,并且缓存起来,这样代码也改得很少,基本符合开闭原则。但是和简单工厂方法相比,实现上差不多,反而增加了代码的复杂度,尤其是在工厂类的实现非常简单的情况下,有点过渡设计了,这时使用简单工厂方法就可以了如果每个解析对象的创建逻辑都比较的复杂,需要组合其他的组件,那么可以考虑工厂方法模式。

四、抽象工厂模式

  抽象工厂模式(Abstract Factory Pattern)用于创建一组相关或相互依赖的对象,它通过提供一个创建一系列相关对象的接口,可以创建不同类型的对象组。
  工厂方法模式已经复杂起来了,抽象工厂模式很大程度是在这个基础上演变的。比如我们是之前是按照后缀名解析的,假设我们同时需要按照内容格式来解析呢?那工程类就变成6个了,这个时候就可以使用抽象工厂模式了。

4.1、内容解析器

  假设我们增加解析器
内容解析器

public interface IContentConfigParser {RuleConfig parse(String configText);
}public class XmlContentConfigParser implements IContentConfigParser {@Overridepublic RuleConfig parse(String configText) {return new RuleConfig();}
}public class YmlContentConfigParser implements IContentConfigParser {@Overridepublic RuleConfig parse(String configText) {return new RuleConfig();}
}public class PropertiesContentConfigParser implements IContentConfigParser {@Overridepublic RuleConfig parse(String configText) {return new RuleConfig();}
}

4.2、工厂类

public interface IConfigParserFactory {IRuleConfigParser createRuleParser();IContentConfigParser createContentParser();
}public class XmlConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new XmlRuleConfigParser();}@Overridepublic IContentConfigParser createContentParser() {return new XmlContentConfigParser();}
}public class YmlConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new YmlRuleConfigParser();}@Overridepublic IContentConfigParser createContentParser() {return new YmlContentConfigParser();}
}public class PropertiesConfigParserFactory implements IConfigParserFactory {@Overridepublic IRuleConfigParser createRuleParser() {return new PropertiesRuleConfigParser();}@Overridepublic IContentConfigParser createContentParser() {return new PropertiesContentConfigParser();}
}

  从而达到减少工厂类的个数,不过实际中抽象工厂模式用得比较少。可能很多小伙伴还是觉得这个工厂模式有问题,比如本文中的工厂或者工厂的工厂缓存,可以使用配置文件+反射去掉,完全适合开闭原则,每个设计模式都有自己的特色和缺点,就看是否被接受。遵循一些原则的同时,需要考虑代码的复杂度,清晰度。

三、优点与缺点

简单工厂模式:

优点:

  • 实现简单,易于理解和使用。
  • 将对象的创建过程封装在工厂类中,客户端无需了解对象创建的具体逻辑。

缺点:

  • 工厂类集中了所有对象的创建逻辑,如果需要添加新产品,可能需要修改工厂类的逻辑,不符合开闭原则。
  • 不符合单一职责原则,因为工厂类既要负责对象的创建,又要负责选择创建哪个对象。

工厂方法模式:

优点:

  • 每个具体工厂只负责创建特定的对象,符合单一职责原则。

  • 可以通过扩展具体工厂类来添加新产品,无需修改现有代码,符合开闭原则。
    缺点:

  • 增加了系统中类的个数,增加了系统的复杂度。

  • 客户端需要知道所使用的具体工厂类,导致与具体工厂类耦合。

抽象工厂模式:

优点:

  • 具有工厂方法模式的所有优点,同时能够创建一组相关的产品对象,形成产品族。
  • 客户端与具体产品的实现解耦,更符合依赖倒置原则。

缺点:

  • 添加新产品族比较复杂,需要修改抽象工厂接口及其所有实现类。
  • 增加系统的抽象性和理解难度,需要同时理解多个抽象类和接口。

  根据实际需求选择适合的工厂模式是非常重要的,简单工厂适用于对象较少且创建逻辑不复杂的情况;工厂方法适用于需要创建多种类型对象的情况;而抽象工厂适用于创建一组相关对象的场景,但要考虑到增加产品的复杂度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/677195.shtml

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

相关文章

什么是系统工程(字幕)23

0 00:00:00,000 --> 00:00:01,617 那这里没有出现 1 00:00:01,617 --> 00:00:04,448 我们可以把它从这里再拖上来 2 00:00:04,448 --> 00:00:06,267 但是这个地方不够 3 00:00:06,267 --> 00:00:08,491 我们把这个地方放大一点 4 00:00:08,491 --> 00:00:11…

分享66个相册特效,总有一款适合您

分享66个相册特效&#xff0c;总有一款适合您 66个相册特效下载链接&#xff1a;https://pan.baidu.com/s/1jqctaho4sL_iGSNExhWB6A?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不…

【java苍穹外卖项目实战二】苍穹外卖环境搭建

文章目录 1、前端环境搭建2、后端环境搭建1、项目结构搭建2、Git版本控制3、数据库创建 开发环境搭建主要包含前端环境和后端环境两部分。 前端的页面我们只需要导入资料中的nginx&#xff0c; 前端页面的代码我们只需要能看懂即可。 1、前端环境搭建 前端运行环境的nginx&am…

中科星图GVE(AI案例)——如何利用ai技术进行船只的识别和提取分析(珠江三角洲)

简介 随着人工智能(AI)技术的快速发展,船只的识别和分析变得越来越容易。AI技术可以利用计算机视觉和深度学习算法来自动识别和提取船只的特征,从而进行进一步的分析。下面将介绍如何利用GVE云计算平台的AI技术进行船只的识别和提取分析。 1. 数据收集与准备: 首先,需要…

STM32 FSMC (Flexible static memory controller) 灵活静态内存控制器介绍

文章目录 1. 介绍FSMC2. FSMC特点3. Block示意图4. AHB接口4.1 Supported memories and transactionsGeneral transaction rulesConfiguration registers 5. 外部设备地址映射5.1 NOR/PSRAM地址映射将NOR Flash/PSRAM的支持进行封装 5.2 NAND/PC Card地址映射 1. 介绍FSMC 说到…

使用python-numpy实现一个简单神经网络

目录 前言 导入numpy并初始化数据和激活函数 初始化学习率和模型参数 迭代更新模型参数&#xff08;权重&#xff09; 小彩蛋 前言 这篇文章&#xff0c;小编带大家使用python-numpy实现一个简单的三层神经网络&#xff0c;不使用pytorch等深度学习框架&#xff0c;来理解…

TELNET 远程终端协议

远程终端协议 TELNET TELNET 是一个简单的远程终端协议&#xff0c;也是互联网的正式标准。 用户用 TELNET 就可在其所在地通过 TCP 连接注册&#xff08;即登录&#xff09;到远地的另一个主机上&#xff08;使用主机名或 IP 地址&#xff09;。 TELNET 能将用户的击键传到…

使用cocos2d-console初始化一个项目

先下载好cocos2d-x的源码包 地址 https://www.cocos.com/cocos2dx-download 这里使用的版本是 自己的电脑要先装好python27 用python安装cocos2d-console 看到项目中有个setup.py的一个文件 python setup.py 用上面的命令执行一下。 如果执行正常的话回出现上面的图 然后…

法国实习面试——计算机相关专业词汇

法语 1.Spcialit - 专业 2.Systme - 系统 3.Embarqus - 嵌入式 4.Logicielle - 软件 5.Distribus - 分布式 6.lectronique - 电子 7.nergie lectrique - 电能 8.Automatisation - 自动化 9.Une exprience de stage - 实习经验 10.Automobiles - 汽车 11.tre charg…

每日五道java面试题之java基础篇(二)

第一题. 为什么说 Java 语⾔“编译与解释并存”&#xff1f; ⾼级编程语⾔按照程序的执⾏⽅式分为编译型和解释型两种。 简单来说&#xff0c;编译型语⾔是指编译器针对特定的操作系统将源代码⼀次性翻译成可被该平台执⾏的机器码&#xff1b;解释型语⾔是指解释器对源程序逐…

前端JavaScript篇之call() 和 apply() 的区别?

目录 call() 和 apply() 的区别&#xff1f; call() 和 apply() 的区别&#xff1f; 在JavaScript中&#xff0c;call()和apply()都是用来改变函数中this指向的方法&#xff0c;它们的作用是一样的&#xff0c;只是传参的方式不同。 call()方法和apply()方法的第一个参数都是…

4核8g服务器能支持多少人访问?2024新版测评

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

js-添加网页快捷方式

title: js-添加网页快捷方式 categories: Javascript tags: [p快捷方式] date: 2024-02-04 15:28:25 comments: false mathjax: true toc: true js-添加网页快捷方式 前篇 谷歌上包困难的情况, 只能通过投放落地页来缓解一下痛苦, web2app 那种形式有几个比较大的缺点就是需要…

MongoDB部署策略

内 容 简 介 本文介绍了MongoDB数据库的优点的数据存储模式的安装部署过程。 利用MongoDB在存储海量数据上的优势&#xff0c;部署存储空间大数据。 欢迎批评指正补充 由于编者水平有限&#xff0c;所搜集资料也很有限&#xff0c;制定的规范肯定有考虑不周全、甚至完全错误…

【Android】GridLayout实现等比布局

GridLayout是十年前的老布局了&#xff0c;因为太久没用过&#xff0c;导致细节方面已经记不太清楚了 初衷是想让GridLayout中每行每列的元素大小都相等&#xff0c;结果怎么试都没有效果 原来是要只设置比例&#xff0c;不设置宽高才行&#xff0c;特意记录下 <GridLayou…

K8S容器挂了后一直重启但不成功原因及解决

K8S容器挂了后一直重启但不成功原因及解决 背景&#xff1a; 应用迁移K8S后&#xff0c;因为POD OOM挂了后&#xff0c;会进行重启&#xff0c;但一直在重启&#xff0c;重启多次都不成功&#xff0c;且应用无报错日志。其他K8S上应用挂了后会自动重启成功。 原因&#xff1…

速过计算机二级python——第9讲 详解第 2 套真题

第9讲 详解第 2 套真题 基本编程题【15 分】简单应用题【25 分】综合应用题【20 分】**问题 1**【10 分】:**问题 2【10 分】:**基本编程题【15 分】 考生文件夹下存在一个文件 PY101.py,请写代码替换横线,不修改其他代码,实现以下功能:【5 分】题目: import __________ b…

指针的学习3

目录 字符指针变量 数组指针变量 二维数组传参的本质 函数指针变量 函数指针变量的创建 函数指针变量的使用 两段有趣的代码 typedef关键字 函数指针数组 转移表 回调函数&#xff1a; 字符指针变量 int main() {char arr[10] "abcdef";char* p1 arr;//…

day36 作用域、作用域链

目录 作用域局部作用域函数作用域块作用域 全局作用域作用域链 作用域 了解作用域对程序执行的影响及作用域链的查找机制&#xff0c;使用闭包函数创建隔离作用域避免全局变量污染。 作用域&#xff08;scope&#xff09;规定了变量能够被访问的“范围”&#xff0c;离开了这个…

【JAVA WEB】盒模型

目录 边框 内边距 基础写法 复合写法 外边距 基础写法 复合写法 块级元素的水平居中 弹性布局 设置行内元素的属性 &#xff0c;span 每一个HTML元素就相当于是一个矩形的“盒子” 这个盒子由以下这几个部分构成&#xff1a; 1.边框 border 2.内容 content 3.内边…