【设计模式】什么场景可以考虑使用简单工厂模式

1.概述

工厂模式是一种创建型模式,主要作用就是创建对象,将对象的创建过程和使用的过程进行解耦。我们平时说的工厂模式实际上是对三种不同类型的工厂模式的统称,简单工厂、工厂方法、抽象工厂,而在23种设计模式中,只定义了工厂方法和抽象工厂,将简单工厂看作是工厂方法的一种特例,本篇主要讲述的是简单工厂

简单工厂,就像它的名字一样突出一个简单,就是将业务流程代码中直接使用new关键字来创建对象,修改为通过一个工厂类创建对象,这就是简单工厂。如果仅仅只是将new操作转移到了一个新的类里面,看起来只是在徒增类的数量和代码量,并没有什么意义。

那为什么我们还要使用简单工厂模式呢?
是因为它在某些特定的场景下有其存在的价值,让我们从不同的场景来看看。

2.从不同场景看简单工厂的意义

2.1.框架或工具的封装

除了日常业务开发之外,有时候我们也可能得做一些框架或者工具的开发,这部分开发出来的工具需要提供给其他的开发人员使用,而他们在使用的时候,第一步就是获取到这个工具的对象,这种情况下就可以给使用者提供一个简单工厂,让使用者通过工厂来创建对象。
例如在Java中使用日历相关的工具Calendar就是通过简单工厂提供的获取日历对象的方法,如下图:
在这里插入图片描述
我们做类似的框架或工具开发的时候,完全可以参照Calendar的方式,给使用者提供创建对象的能力,从而让使用者无需关心对象的实际创建过程,而是只需要通过特定的方法和参数,就能获取到一个可供使用的对象。

2.2.复杂业务对象的创建

即使是在做业务相关开发,有时候也会涉及到一些相对复杂的业务对象的创建(例如DDD中的领域对象),这时候可以使用简单工厂将产品对象的创建流程从业务流程中抽离。由于创建对象的复杂性被隔离在工厂类中,因此当涉及到产品类的变化时,比如增加新功能、改变实现方式等,只需要改动工厂类,不会对使用产品的其他模块造成影响,有利于产品创建逻辑的集中管理,以及系统的维护和版本升级。


之前在做一个项目开发的时候使用过简单工厂处理过Domain对象的创建,先说一下背景:

这个项目使用的时COLA架构(一种DDD的代码层面实践的框架),在这个架构的分层中Domain层属于最底层,如下图中的demoWeb-domain
在这里插入图片描述
Domain层中会完成大部分的业务逻辑,但我们知道业务流程中往往伴随着与中间件其他服务之间的交互(例如数据库的存取操作),但这部分交互不应该由领域对象来实现。

COLA中的做法就是提供一个“防腐层”来实现,所谓的防腐层就是在domain层中定义与中间件交互的interface再交由infrastructure来实现,这样在domain层就只需要关心自己需要做一个什么交互,而不需要关心具体的交互实现,如下的红框所示。
在这里插入图片描述
背景介绍完了,说一下这里面存在一个问题,就是Domain对象为了保证业务模型的纯洁性,一般不会使用Spring来管理领域对象的生命周期,在这种情况下如何才能将gatewayImpl对象注入到领域对象中呢?

答案是在Infrastructure层中,将getewayImpl对象set到领取对象中,如果领域对象的创建过程相对复杂,就可以使用简单工厂进行创建,统一管理创建逻辑,代码如下:

@Component
public class MsgWecomAppFactory {@Resourceprivate MsgWecomGateway msgWecomGateway;@Resourceprivate MsgWecomCacheGateway msgWecomCacheGateway;@Resourceprivate WecomHttpHelper wecomHttpHelper;/*** 创建企业微信应用号领域对象,用于确认消息** @param agentId 应用号ID* @return 领域对象*/public MsgWecomApp createMsgWecomAppForConfirm(String agentId) {MsgWecomApp msgWecomApp = new MsgWecomApp();// 1.查询企业微信应用配置信息Optional<MsgWecomAppConfig> appConfig = msgWecomGateway.getWecomAppConfigByAgentId(agentId);Assert.isTrue(appConfig.isPresent(), "企业微信应用号配置信息不存在,执行失败,agentId:" + agentId);msgWecomApp.setConfig(appConfig.get());msgWecomApp.setMsgWecomCacheGateway(msgWecomCacheGateway);msgWecomApp.setMsgWecomGateway(msgWecomGateway);msgWecomApp.setWecomHttpHelper(wecomHttpHelper);return msgWecomApp;}/*** 创建企业微信应用号领域对象,用于发送消息** @param agentId 应用号ID* @param phones  手机号* @return 领域对象*/public MsgWecomApp createMsgWecomAppForSend(String agentId, Set<String> phones) {MsgWecomApp msgWecomApp = new MsgWecomApp();// 1.查询企业微信应用配置信息Optional<MsgWecomAppConfig> appConfig = msgWecomGateway.getWecomAppConfigByAgentId(agentId);Assert.isTrue(appConfig.isPresent(), "企业微信应用号配置信息不存在");msgWecomApp.setConfig(appConfig.get());// 2.查询用户信息,优先使用手机号查询,如果手机号为空或手机号未查询到则使用邮箱查询List<MsgWecomMemberInfo> msgWecomMemberInfos = new ArrayList<>();if (CollUtil.isNotEmpty(phones)) {msgWecomMemberInfos = msgWecomGateway.listWecomMemberInfoByPhone(phones);// 对比参数中的手机号与查询到的手机号,获取差集List<String> existPhone = msgWecomMemberInfos.stream().map(MsgWecomMemberInfo::getPhone).collect(Collectors.toList());msgWecomApp.setNotExistPhones(CollUtil.subtractToList(phones, existPhone));}msgWecomApp.setToSendMemberInfos(msgWecomMemberInfos);msgWecomApp.setMsgWecomCacheGateway(msgWecomCacheGateway);msgWecomApp.setMsgWecomGateway(msgWecomGateway);msgWecomApp.setWecomHttpHelper(wecomHttpHelper);return msgWecomApp;}
}

2.3.与其他模式的组合使用

策略模式中的选择器实现:
在SpringBoot优雅使用策略模式这一篇博客中提到了如何使用Spring对Bean的管理能力,来实现策略模式的选择器。同样的,如果没有使用Spring或者业务对象的生命周期不需要Spring框架介入时,就可以使用简单工厂+单例的方式来实现,代码如下:

/*** 策略选择器工厂*/
public class StrategySelectorFactory {private static final Map<String, Strategy> STRATEGY_MAP = new java.util.HashMap<>();static {STRATEGY_MAP.put("A", new StrategyA());STRATEGY_MAP.put("B", new StrategyB());}public static Strategy getStrategy(String strategyKey) {if (strategyKey == null || strategyKey.isEmpty()) {throw new IllegalArgumentException("strategyKey can not be empty");}return STRATEGY_MAP.get(strategyKey);}
}

3.总结

本篇主要讲述了工厂模式中的特例:简单工厂模式,并通过3种不同的场景来介绍这种模式存在的意义,有以下几方面:

  • 封装对象创建过程: 将对象的创建过程封装工厂类中,使用者无需了解具体产品的创建细节,只需调用工厂类提供的静态方法即可得到所需的产品对象。这样可以隐藏产品类的具体实现,降低耦合度。
  • 控制逻辑集中: 将复杂对象的创建对象逻辑集中在工厂类中,如果需要修改或扩展产品类型时,只需要修改工厂类中的代码。这使得添加新产品或者调整产品创建逻辑更加方便、集中管理,也有利于系统的维护和版本升级
  • 组合其他模式满足特定的需求: 在某些应用场景中,如根据参数动态选择不同类型的对象实例化,简单工厂+单例提供简洁有效的解决方案,避免了直接使用 new 关键字创建对象带来的硬编码问题。

最后,虽然简单工厂模式在一定程度上提高了灵活性和可维护性,但它也有其局限性,例如违反了开闭原则,每增加一个新产品就需要修改工厂类的代码。

但瑕不掩瑜,简单工厂模式在很多简单场景下发挥着重要的作用。

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

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

相关文章

深度学习笔记(六)——网络优化(2):参数更新优化器SGD、SGDM、AdaGrad、RMSProp、Adam

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 截图和程序部分引用自北京大学机器学习公开课 在前面的博文中已经学习了构建神经网络的基础需求&#xff0c;搭建了一个简单的双层网络结构来实现数据的分…

机器学习~从入门到精通(三)梯度下降法

一、梯度下降法 # 梯度下降不是一种算法&#xff0c;是一种最优化方法 # 上节课讲解的梯度下降的案例 是一个简单的一元二次方程 # 最简单的线性回归&#xff1a;只有一个特征的线性回归&#xff0c;有两个theta # 二、在多元线性回归中使用梯度下降求解 三、### R…

求斐波那契数列矩阵乘法的方法

斐波那契数列 先来简单介绍一下斐波那契数列&#xff1a; 斐波那契数列是指这样一个数列&#xff1a;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……这个数列从第3项开始 &…

行业内参~移动广告行业大盘趋势-2023年12月

前言 2024年&#xff0c;移动广告的钱越来越难赚了。市场竞争激烈到前所未有的程度&#xff0c;小型企业和独立开发者在巨头的阴影下苦苦挣扎。随着广告成本的上升和点击率的下降&#xff0c;许多原本依赖广告收入的创业者和自由职业者开始感受到前所未有的压力。 &#x1f3…

使用pygame实现简单的烟花效果

import pygame import sys import random import math# 初始化 Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption("Fireworks Explosion")# 定义颜色 black (0, 0, 0) wh…

基于Java SSM框架实现在线作业管理系统项目【项目源码】计算机毕业设计

基于java的SSM框架实现在线作业管理系统演示 JSP技术 JSP技术本身是一种脚本语言&#xff0c;但它的功能是十分强大的&#xff0c;因为它可以使用所有的JAVA类。当它与JavaBeans 类进行结合时&#xff0c;它可以使显示逻辑和内容分开&#xff0c;这就极大的方便了运动员的需求…

IPv6组播--SSM Mapping

概念 SSM(Source-Specific Multicast)称为指定源组播,要求路由器能了解成员主机加入组播组时所指定的组播源。 如果成员主机上运行MLDv2,可以在MLDv2报告报文中直接指定组播源地址。但是某些情况下,成员主机只能运行MLDv1,为了使其也能够使用SSM服务,组播路由器上需要提…

k8s-数据卷

存储卷----数据卷 容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的&#xff0c;delete&#xff0c;k8s用控制创建的pod&#xff0c;delete相当于重启&#xff0c;容器的状态也会恢复到初识状态 一旦容器回到初始状态&#xff0c;所有得分后天编辑的文件…

粒子群算法优化RBF神经网络回归分析

目录 完整代码和数据下载链接:粒子群算法优化RBF神经网络回归分析(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88738570 RBF的详细原理 RBF的定义 RBF理论 易错及常见问题 RBF应用实例,基于rbf的空调功率预测 代码 结果分析 展望…

通过Wireshark抓包分析谈谈DNS域名解析的那些事儿

原创/朱季谦 本文主要想通过动手实际分析一下是如何通过DNS服务器来解析域名获取对应IP地址的&#xff0c;毕竟&#xff0c;纸上得来终觉浅&#xff0c;绝知此事要躬行。 一、域名与IP地址 当在浏览器上敲下“www.baidu.com”时&#xff0c;一键回车&#xff0c;很快&#x…

Linux远程登陆协议ssh

目录 一、SSH服务 1. ssh基础 2. 原理 3. 服务端配置 3.1 常用配置项 3.2 具体操作 3.2.1 修改默认端口号 3.2.2 禁止root用户登录 3.2.3 白名单列表 3.2.4 黑名单列表 3.2.5 使用秘钥对及免交互验证登录 3.2.6 免交互式登录 一、SSH服务 1. ssh基础 SSH&…

VQE音频处理流程

VQE 上行VQE&#xff0c;主要针对MIC采集部分的音频增强 下行VQE&#xff0c;主要针对SPK播放部分的音频增强 附关键词解释 RES RES 模块为重采样&#xff08;Resampler&#xff09;模块。当AI上行或AO下行通路中开启VQE 各功能 模块时&#xff0c;在处理前后各存在一次重采样…

c语言实现b树

概述&#xff1a;B 树&#xff08;B-tree&#xff09;是一种自平衡的搜索树数据结构&#xff0c;广泛应用于数据库和文件系统等领域。它的设计旨在提供一种高效的插入、删除和查找操作&#xff0c;同时保持树的平衡&#xff0c;确保各个节点的深度相差不大。 B 树的特点包括&a…

怎么使用好爬虫IP代理?爬虫代理IP有哪些使用技巧?

在互联网时代&#xff0c;爬虫技术被广泛应用于数据采集和处理。然而&#xff0c;在使用爬虫技术的过程中&#xff0c;经常会遇到IP被封禁的问题&#xff0c;这给数据采集工作带来了很大的困扰。因此&#xff0c;使用爬虫IP代理成为了解决这个问题的有效方法。本文将介绍如何使…

【redis基础1】基础数据类型详解和应用案例

博客计划 &#xff0c;我们从redis开始&#xff0c;主要是因为这一块内容的重要性不亚于数据库&#xff0c;但是很多人往往对redis的问题感到陌生&#xff0c;所以我们先来研究一下。 本篇&#xff0c;我们先看一下redis的基础数据类型详解和应用案例。 1.redis概述 以mysql为…

xtu oj 1340 wave

题目描述 一个n列的网格&#xff0c;从(0,0)网格点出发&#xff0c;波形存在平波(从(x,y)到(x1,y))&#xff0c;上升波(从(x,y)到(x1,y1))&#xff0c;下降波(从(x,y)到(x1,y−1))三种波形&#xff0c;请问从(0,0)出发&#xff0c;最终到达(n,0)的不同波形有多少种&#xff1f…

C++PythonC# 三语言OpenCV从零开发(1):环境配置

文章目录 前言课程选择环境配置PythonC#COpenCV官网下载新建C项目测试运行Csharp版Python版 gitee仓库总结 前言 由于老王我想转机器视觉方向的上位机行业&#xff0c;我就打算开始从零学OpenCV。但是目前OpenCV有两个官方语言&#xff0c;C和Pyhont。C# 有大佬做了对应的Open…

vue 自定义网页图标 favicon.ico 和 网页标题

效果预览 1. 添加配置 vue.config.js 在 module.exports { 内添加 // 自定义网页图标pwa: {iconPaths: {favicon32: "./favicon.ico",favicon16: "./favicon.ico",appleTouchIcon: "./favicon.ico",maskIcon: "./favicon.ico",msTil…

memory泄露分析方法(java篇)

#memory泄露主要分为java和native 2种&#xff0c;本文主要介绍java# 测试每天从monkey中筛选出内存超标的app&#xff0c;提单流转到我 首先&#xff0c;辨别内存泄露类型&#xff08;java&#xff0c;还是native&#xff09; 从采到的dumpsys_meminfo_pid看java heap&…

【ROS2】使用C++实现简单的发布订阅方

1 构建自定义数据类型 1、自定义消息类型Student 1.1 创建base_interfaces_demo包 1.2 创建Student.msg文件 string name int32 age float64 height 1.2 在cmakeLists.txt中增加如下语句 #增加自定义消息类型的依赖 find_package(rosidl_default_generators REQUIRED) # 为…