将简单工厂模式改造应用到项目中,而不是纸上谈兵

news2024/11/28 20:34:44

10月26日晚补充:经过掘友的提醒,我才发现之前我这篇所写的策略模式,其本身更偏向于工厂模式,我起初以为是掘友分不清工厂模式和策略模式,实际上是我自己把自己绕进去,看不清工厂模式和策略模式的区别

因此出现的错误,为此感到十分的抱歉,实属学艺不精,下次在输出自己的知识时,一定会进行内容正确性的确认,一定一定会多次审稿

非常感谢小伙伴给予的提醒,写下此段文字,也是为了警醒自己,以免再犯此类错误

这篇文章的产出也是最近在写代码的时候,遇到的一个很简单的问题,也是大家嘴边常常挂着的if...else if...else问题。

其实一两个if...else if...else也没啥问题的,如果好几个地方用到了的话, 就显得有些磨人了,每次执行那一步操作之前,都必须先判断一遍~。

但其实肿么说勒,在你不知道可以优化代码的方式时,你不会觉得自己写出来的代码有啥问题,甚至还会觉得自己写的还不错。(说的是我自己啦...)

但是如果你知道可以把这件事情做的更好一些,然后你再回头看看自己为了完成任务写出的代码时,就有种自己看着自己把代码写成一副烂代码的感觉

所以我觉得多学一点设计模式和工作所结合,作用还是特别大的

开始的缘由

让我去整理一个上传文件的模块,然后项目中的话,就本地和云都混合的,并且也没有确定一定是使用某个存储服务。

目前的话,就支持local、minio、阿里云,但是我在另外的一个项目中(不是现在我弄的项目),看到了七牛云

项目中就写了一个所谓的全局上传的方法:

 public static String upload(MultipartFile file, String bizPath, String uploadType) {
     String url = "";
     if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
         url = MinioUtil.upload(file,bizPath);
     }else{
         url = OssBootUtil.upload(file,bizPath);
     }
     return url;
 }
复制代码

本地上传接口没囊括在内....,是嵌入在业务代码中判断的。

咋一看这个代码,其实不会觉得有啥的,哈哈

肿么说我自己勒,我在最开始的时候,就按照这个方法,在业务代码中嵌入了一些判断,然后调用的这个方法去上传。


等到功能实现之后,再回头看自己写的代码,是真的觉得....,然后就想动一动它。

在这期间也看了许多文章。

推荐大佬的文章:

实战!工作中常用到哪些设计模式 作者: 捡田螺的小男孩

代码中的问题

我们先说说上面代码中存在的问题:

  1. 如果我后期需要增加一个七牛云的文件存储服务,是不是必须要去动这段代码,不动的话,就只能在业务增加逻辑判断。
  2. 其次,我截图的只是upload这一个方法,其他例如delete、download等等,都需要改动代码去增加一段逻辑。

多个扩展就需要做多个判断,改多次代码。

在维护期间,其实在能不改动源代码的情况下,尽量不要去改动源代码,一定确定能对还好,怕就怕改出了问题,就糟糕了。

并且在设计原则与思想中就有这一点,对开闭原则( 对扩展开放、修改关闭)

开闭原则如何理解呢?

软件实体(模块、类、方法等)应该“对扩展开放、对修改关闭” .

添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。

当然开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。

简单工厂模式

关于这个工厂模式,其实我一年前记过一篇刚学的笔记Java设计模式-工厂模式,当时就是觉得学到了那个阶段,顺着学了一遍,但都是囫囵吞枣,一知半解

所以今天才是实战篇~ 我觉得是有效且可以应用上的一篇文章

当然很多设计模式的目的之一都是为了实现代码的扩展性。

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。

说概念不好说,我们还是直接上代码~

简单工厂模式本身并不完全实现“开闭原则”,当然在我这个代码里面,也并没有完全实现开闭原则,但是可以说做较小的变动,去达到类似实现开闭原则的方法。

简单工厂模式改良代码的实现

1、类图

先来整个类图:

(图片说明:类图本身不应该有颜色的,此处是我为了观感更舒适而添加的)

整体不难~

2、编写一个统一的上层接口

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 19:53
  */
 public interface FileStoreManagerService {
 ​
     // 此处就是一个枚举类,在后文中
     UploadTypeEnum getUploadType();
 ​
     void upload();
 ​
     void download();
 ​
     void deleteFile();
 ​
     List<String> getFiles();
 }
复制代码

我们目前有aliyun、minio、local三种存储文件的方式,因此我们第二步就是分别编写这三个服务类。

3、编写相关的实现类

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 19:59
  */
 @Service
 public class AliyunFileServiceImpl implements FileStoreManagerService {
 ​
     @Override
     public UploadTypeEnum getUploadType() {
         return UploadTypeEnum.ALIYUN;
     }
     @Override
     public void upload() {
         System.out.println("调用aliyun的upload方法");
     }
 ​
     @Override
     public void download() {
         System.out.println("调用aliyun的download方法");
     }
 ​
     @Override
     public void deleteFile() {
         System.out.println("调用aliyun的deleteFile方法");
     }
 ​
     @Override
     public List<String> getFiles() {
         System.out.println("调用aliyun的getFiles方法");
         return new ArrayList<>();
     }
 }
复制代码
 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 20:00
  */
 @Service
 public class LocalFileServiceImpl implements FileStoreManagerService {
 ​
     @Override
     public UploadTypeEnum getUploadType() {
         return UploadTypeEnum.LOCAL;
     }
 ​
     @Override
     public void upload() {
         System.out.println("调用Local的upload方法");
     }
 ​
     @Override
     public void download() {
         System.out.println("调用Local的download方法");
     }
 ​
     @Override
     public void deleteFile() {
         System.out.println("调用Local的deleteFile方法");
     }
 ​
     @Override
     public List<String> getFiles() {
         System.out.println("调用Local的getFiles方法");
         return new ArrayList<>();
     }
 }
复制代码
 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 19:56
  */
 @Service
 public class MinioServiceImpl implements FileStoreManagerService {
     @Override
     public UploadTypeEnum getUploadType() {
         return UploadTypeEnum.MINIO;
     }
 ​
     @Override
     public void upload() {
         System.out.println("调用minio的upload方法");
     }
 ​
     @Override
     public void download() {
         System.out.println("调用minio的download方法");
     }
 ​
     @Override
     public void deleteFile() {
         System.out.println("调用minio的deleteFile方法");
     }
 ​
     @Override
     public List<String> getFiles() {
         System.out.println("调用minio的getFiles方法");
         return new ArrayList<>();
     }
 }
复制代码

编写完真正实现的服务类后,我们还需要一个方式来获取这些具体的服务类。

比如我们可以实现ApplicationContextAware接口,把那些具体的实现的服务类,初始化到map里面,等到我们要使用时再通过map直接获取即可。

4、使用前的准备

我原本想把它叫做初始化的,但是想了想,不太符合,就还是用了这个小标题吧。

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 20:04
  */
 @Component
 public class FileStoreManagerFactoryService implements ApplicationContextAware {
 ​
     private Map<UploadTypeEnum,FileStoreManagerService> fileStoreManagerStrategy =new HashMap<>();
 ​
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         Map<String, FileStoreManagerService> tmepMap = applicationContext.getBeansOfType(FileStoreManagerService.class);
         tmepMap.values().forEach(strategyService -> fileStoreManagerStrategy.put(strategyService.getUploadType(), strategyService));
     }
 ​
     public FileStoreManagerService getFileStoreManagerService(UploadTypeEnum uploadTypeEnum){
         return fileStoreManagerStrategy.get(uploadTypeEnum);
     }
 }
复制代码

ApplicationContextAware是在spring初始化完bean后才注入上下文的,所以在注入的上下文中,已经将我们具体的实现类加载好啦~

5、使用

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 20:39
  */
 @RunWith(SpringRunner.class)
 @SpringBootTest(classes = {DemoApplication.class})
 public class DemoTest {
 ​
     @Autowired
     private FileStoreManagerFactoryService fileStoreManagerFactoryService;
 ​
     private final String uploadType="aliyun";
 ​
     @Test
     public void test1(){
         UploadTypeEnum uploadTypeEnum = DescribableEnum.getByProperty(UploadTypeEnum.class, u -> u.getName(), uploadType);
         System.out.println(uploadType);
         FileStoreManagerService fileStoreManagerService = fileStoreManagerStrategyService.getFileStoreManagerService(uploadTypeEnum);
         fileStoreManagerService.upload();
         /**
          * out:
          * aliyun
          * 调用aliyun的upload方法
          */
     }
 }
复制代码

在测试代码中可以看到,我们直接通过fileStoreManagerFactoryService.getFileStoreManagerService(uploadTypeEnum)方法就可以获取到我们想要的实现类,完全不需要我们进行繁琐的if...else去判断到底该new哪个实现类。

这种思想贯穿于诸多框架之中,并且我敢肯定的是,你看到我编写的使用前的准备那段代码时,会觉得意外的熟悉。因为Spring生态中太多地方在使用啦,只是很少整体的去看待。

补充:枚举类的代码和使用,在上一篇文章中,因为更多的关乎思路,就没有将这些贴出来占篇幅了。

要看的可以点击👉你知道 Java 中关键字 enum 是一个语法糖吗?反编译枚举类

6、扩展

假如我现在要在之前的代码中,增加一个七牛云的服务,

  • 那么我们需要增加一个七牛云的服务实现类
  • 其次在枚举类中增加一个qiniuyun的枚举值(也是因为此处对枚举类做了修改,所以并没有完全实现开闭原则)

其他的该肿么使用就还是肿么使用。

 /**
  * @description:
  * @author: Ning Zaichun
  * @date: 2022年10月25日 20:00
  */
 @Service
 public class QiniuyunServiceImpl implements FileStoreManagerService {
     @Override
     public UploadTypeEnum getUploadType() {
         return UploadTypeEnum.QINIUYUN;
     }
     @Override
     public void upload() {
         System.out.println("调用Qiniuyun的upload方法");
     }
     @Override
     public void download() {
         System.out.println("调用Qiniuyun的download方法");
     }
 ​
     @Override
     public void deleteFile() {
         System.out.println("调用Qiniuyun的deleteFile方法");
     }
     @Override
     public List<String> getFiles() {
         System.out.println("调用Qiniuyun的getFiles方法");
         return new ArrayList<>();
     }
 }
复制代码

总结

工厂模式和策略模式的主要区别就是:工厂模式的主要目的是创建对象,策略模式的主要目的是定义一个上层行为。在策略模式中,我们不应该实际掉调用实现类,而是调用策略接口即可。

可以看一下这篇文章: 犯错总结--工厂模式和策略模式傻傻没分清

小伙伴说的这篇文章像工厂又像策略,是没有错的,其实两者模式都有参考,文章开头已说,更偏向于工厂模式,因为我的目的是根据不同行为创建对象,我的目的是创建对象,所以从这点来说更偏向于工厂模式。 一套组合拳,也是一个缝合怪~ 哈哈

但是又因为不同行为,这点又得说到了策略模式,所以看起来你说是策略模式,它也有,说起来是工厂模式,它也是,所以我在标题中加了改造两字。

设计模式本来就是一套组合拳,不同的开发者有不同的使用方法,不同的看法。

我也是碰巧遇到,也是正好看到,就这么去干啦~

更好的话语,说太不出,希望各位理解。

也希望大家在阅读的时候,带着自己的思考去评判每篇文章

将自己学到的知识,融合到自己的实践中,永远都是最好的方式。

“实践是检验真理的唯一标准”,学了许许多多的理论,但无处实施的话,那也只是纸上谈兵罢啦。

纸上得来终觉浅,绝知此事要躬行

后记

今天就写到了这里啦~ 感觉自己还好菜啊~ 一起努力哦~

希望你是满载而归的~

对啦,分享一件小小的快乐,今天在自己的手机上看到了掘金 app 推送了自己的文章,被认可的感觉真的非常非常开心,我们一起加油吧。

在这不如意的生活里,我们也要尽情热爱生活啊,用文字去表达,用照片去记录,用身体去感受,用爱去接受爱,你会发现一切都是如此值得热爱啊

不开心就去跑跑步吧~

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

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

相关文章

网闸的工作原理

网闸GAP由固态读写开关和存储人质系统组成&#xff0c;其中固态开关的转换效率达到了纳秒级&#xff0c;存储介质通常采用scsi硬盘&#xff0c;因此GAP的性能得到了保证。 GAP连接在两个独立的网络系统中间&#xff0c;内网与外网永远不同时连接&#xff0c;在同一时刻只有一个…

鱼传科技:函数计算,只要用上就会觉得香

深圳鱼传科技有限公司是专注以精准营销和互联网生态产品运营为核心的综合互联网营销推广服务商。通过整合全网优质媒体资源&#xff0c;并结合智能数据模型和 AI 标签算法&#xff0c;向企业提供包括流量矩阵搭建运营、媒介流量采买、投放模型设计、产品营销策划、数据监控分析…

面试官:如何解决 Redis 数据倾斜、热点等问题

Redis 作为一门主流技术&#xff0c;应用场景非常多&#xff0c;很多大中小厂面试都列为重点考察内容 前几天有小伙伴学习时&#xff0c;遇到下面几个问题&#xff0c;来咨询小编 考虑到这些问题比较高频&#xff0c;工作中经常会遇到&#xff0c;这里写篇文章系统讲解下 问…

健身房头戴式耳机好吗、最优秀的健身房耳机推荐

作为一名运动爱好者&#xff0c;无论是在户外跑步、骑行&#xff0c;还是在室内健身&#xff0c;耳机都是平日运动时不可或缺的装备。即使在日常通勤中&#xff0c;很多人也不能缺少它的陪伴。普通的耳机很好选择&#xff0c;只需要看音质好不好就可&#xff0c;运动耳机就比较…

win10系统下安装JDK1.8及配置环境变量的方法

1&#xff1a;下载安装包 地址&#xff1a;https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html 选择与自己环境相匹配的安装包 后续过程需要登录Oracle账号&#xff0c;如果你网络环境不好或者没有Oracle账号&#xff0c;请按照文末默认获取方式获…

ssm+Vue计算机毕业设计校园闲置物品交易系统(程序+LW文档)

ssmVue计算机毕业设计校园闲置物品交易系统&#xff08;程序LW文档&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项…

GCSE英语语言考试-叙述视角

What is narrative voice? Narrative voice is the perspective the story is told from. The writer chooses a narrative voice carefully, as it can have an important effect on the story and the reader’s response. 什么是叙述视角&#xff1f; 叙述视角是讲述故事的…

机器人逆向运动学(IK)入门:问题分析

在游戏开发和机器人开发中进场需要用到的一个场景是&#xff0c;给机器人或者游戏角色末位置的位置和姿态&#xff0c;求解可到达给定位置和姿态的各关节的角度值&#xff0c;这里就需要逆向运动学去求解这个问题&#xff0c;在场景中有着很多的应用&#xff0c;比如说六轴机械…

电脑重装系统蓝屏详细解决方法分享

我们在使用电脑时我们经常会遇到各种问题&#xff0c;比如卡顿&#xff0c;死机&#xff0c;蓝屏&#xff0c;黑屏等等。今天这里小编为大家介绍的是电脑蓝屏的问题&#xff0c;电脑蓝屏会导致数据丢失、未保存的文件丢失、游戏关键时刻坑队友等情况。接下来&#xff0c;小编就…

使用Github Actions自动部署vue项目到nginx服务器

解决的问题&#xff1a;妈妈再也不担心我deploy时候手滑了 1. 避免手动执行重复的前端发布流程&#xff0c;节约开发时间和耐心 2. 减少了使用Jenkins类似的工具来做这种简单的发布流程&#xff0c;减少了第三方系统&#xff08;Jenkins&#xff09;的维护成本 前置条件 1.…

MySQL数据库学习(7)

一、简介 MySQL是最流行的关系型数据库管理系统&#xff0c;在这里我使用 PyMySQL 连接数据库&#xff0c;并实现简单的增删改查。 PyMySQL是在 Python3.x版本中用于连接 MySQL服务器的一个库&#xff0c;Python2中则使用 mysqldb。 PyMySQL遵循 Python数据库 API v2.0规范&…

保命小诀窍:IDEA远程Debug技巧,你了解吗?

前言 昨天看到一个问题&#xff0c;“疫情结束后你最想吃什么&#xff1f;” 仔细想了一下&#xff0c;火锅&#xff1f;烤肉&#xff1f; 看了一下体重秤&#xff0c;怕是只能报个健身房了。 你以为你胖N斤的时间复杂度是O(2^N)&#xff0c; 事实上它是O(1)&#xff0c;嗖…

MySQL——MySQL的flush

有时候会出现这么一种情况&#xff1a;一条 SQL 语句&#xff0c;正常执行的时候特别快&#xff0c;但是有时也不知道怎么回事&#xff0c;它就会变得特别慢&#xff0c;并且这样的场景很难复现&#xff0c;它不只随机&#xff0c;而且持续时间还很短。 SQL语句为何变慢了 根…

Oracle单机部署:数据库安装

Oracle单机部署&#xff1a;数据库安装安装前须知数据库字符集自动内存管理数据库安装配置图形化安装安装后检查&#x1f42c; 使用oracle用户来安装数据库。 安装前须知 数据库字符集 在创建数据库之后&#xff0c;更改字符集在时间和资源上的代价都是非常昂贵的。可能需要…

全球银行最大分布式核心系统全面上线,邮储银行做到了!

摘要&#xff1a;近年来&#xff0c;国家陆续出台金融科技相关政策&#xff0c;提出创新驱动发展战略&#xff0c;强调以新一代信息和网络技术为支撑&#xff0c;拓展互联网金融&#xff0c;促进技术创新和商业模式创新的融合。本文分享自华为云社区《全球银行最大分布式核心系…

算法设计 - 01背包问题

学习来源 【自制】01背包问题算法动画讲解_哔哩哔哩_bilibili 问题描述 有N件物品&#xff0c;第i件物品的重量是w[i]&#xff0c;价值是p[i]。 有一个背包&#xff0c;背包的承重是W。 求解&#xff1a;将哪些物品装入背包可获得最大价值。 实例说明 有如下物品&#xff…

尚医通-MyBatis-Plus:条件查询Wapper(五)

&#xff08;1&#xff09;MyBatis-Plus&#xff1a;条件查询 实现对数据库复杂的条件操作&#xff1a; Wrapper &#xff1a; 条件构造抽象类&#xff0c;最顶端父类 AbstractWrapper &#xff1a; 用于查询条件封装&#xff0c;生成 sql 的 where 条件 QueryWrapper &…

【文献研究】车辆路径问题静态合作博弈的数学模型及算法实现

前言&#xff1a;以2015年发表在期刊《MATHEMATICAL GAME THEORY AND APPLICATIONS》上的文章《Strong Coalitional Equilibrium in a Transportation Game》为学习资料&#xff0c;学习关于车辆路径模型的合作博弈。以下是本人对文献内容的一些粗浅理解&#xff0c;由于本人英…

linux批量操作文件命令总结

总结下常用的linux命令&#xff0c;linux下的命令组合着实强大。有时候即便是使用的windows系统也可以在Dos窗口下使用linux下的一些命令工具&#xff0c;完成一些文本日常处理。 查找所有文件 find ./ -name "*.log" 查找某一后缀的文件并删除 find ./ -name &qu…

shell篇---运行python,单个或多个

shell运行python1、方法1在shell中利用export指定python解释器路径2、方法2利用conda激活虚拟环境&#xff0c;进入文件执行目录执行3、多个python&#xff08;具体例子&#xff09;4、具体例子code启动查看1、方法1 在shell中利用export指定python解释器路径 编写如下run.sh…