百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

学会将策略模式应用到项目中,而不是纸上谈兵

lipiwang 2024-10-31 15:24 15 浏览 0 评论

这篇文章的产出也是最近在写代码的时候,遇到的一个很简单的问题,也是大家嘴边常常挂着的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设计模式-策略模式,当时就是觉得学到了那个阶段,顺着学了一遍,但都是囫囵吞枣,一知半解

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

而策略模式又恰好是实现开闭原则的其中的一种模式, 当然很多设计模式的目的之一都是为了实现代码的扩展性。

策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

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

策略模式代码实现

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 FileStoreManagerStrategyService 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 FileStoreManagerStrategyService fileStoreManagerStrategyService;
 ?
     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方法
          */
     }
 }
复制代码

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

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

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

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

6、扩展

之前还说到了策略模式是遵守了开闭原则的~

假如我现在要在之前的代码中,增加一个七牛云的服务,那么我们只需要增加一个七牛云的服务实现类(枚举类增加一个七牛云的枚举值)就完事了,其他的该肿么使用就还是肿么使用。

 /**
  * @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<>();
     }
 }
复制代码

demo项目的结构

总结

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

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

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


原文链接:https://juejin.cn/post/7158472864499236900

相关推荐

想减少Windows 11内存占用?请取消固定Teams

如果你想要提高Windows11系统的运行速度,那么可以禁用某些默认启用的功能和设置。如果你的Windows11是安装在已经停止支持的设备或者内存容量不高的旧设备,那么应该立即限制或禁用固...

Windows查看端口占用、查看PID对应的进程、并终止进程

Windows下:查看端口占用netstat-ano|findstr"端口号"获取到pid查看PID对应的进程tasklist|findstr"进程ID"...

计算机组成原理(36): 分时之一——进程

建立一个虚拟机VM目标:给每个程序一个自己的虚拟机“VirtualMachine”,程序并不知道其他的虚拟机。1.1进程(Process)为了捕获正在运行的程序,我们创建一个称为“进程(Proce...

window系统如何停止端口被占用的进程(高手版)

如上图1,作为开发人员是不是经常遇到这个问题?(Webserverfailedtostart.Port9527wasalreadyinuse.)当然,如果在你知道确实有某个进程正占...

电脑的文件无法删除咋回事?你需要这款神兵利器

很多朋友用电脑的时候,都遇到过文件无法删除的情况。这往往是由于文件被某个软件、进程所调用所引发的——在Windows中,某个文件如果被使用,这个文件可能就没法进行删除、重命名之类的操作了。想要进一步操...

Windows日志分析(windows 日志文件)

1.Windows日志文件简介1.1Windows日志核心分类1.系统日志系统日志包含由Windows系统组件记录的事件,记录系统进程和设备驱动程序的活动。由它审核的系统事件包括启动失败的设备驱动程...

电脑软件崩溃、闪退不用慌!DJS Tech 教你几招轻松解决

当你正全神贯注用电脑处理重要文件、沉浸在精彩的游戏世界,或是观看喜欢的视频时,软件突然崩溃、闪退,那一刻的烦躁简直难以言喻。别着急,DJSTech作为深耕计算机领域多年的专业团队,为你带来一系列超...

微软Win11推进淘汰控制面板,时间服务器配置迁移至设置应用

IT之家5月29日消息,科技媒体Winaero昨日(5月28日)发布博文,报道称微软在Windows11系统中,继续推进“淘汰控制面板”进程,配置时间服务器地址选项迁移到设置应...

微软 PowerToys更新,可帮你找出 Win11上哪些进程正在占用该文件

IT之家11月3日消息,微软针对Windows11和Windows10的PowerToys已经更新到了最新的0.64.0版本,并上线了一个名为“文件锁匠FileLock...

Windows基础操作 认识任务管理器(windows任务管理器的使用)

Windows基础操作:认识任务管理器任务管理器(TaskManager)是Windows系统中一个功能强大的实用工具,它为用户提供了实时监控系统资源、管理正在运行的程序和服务的能力。掌握任务管理器...

windows——netstat过滤(终止进程)

windows——netstat过滤(终止进程)在Windows操作系统中,使用netstat命令可以查看网络连接的状态。要过滤特定协议或端口的连接,可以使用以下命令:查看所有连接:netstat-...

只要这么做 Windows Defender与第三方就能和平共存啦

无论大家是否喜欢WindowsDefender,伴随着Windows10的不断升级,它已经成为系统的底层必备组件之一。虽然我们有各种各样的方法去关闭它,换用顺手的第三方,但只要更新打补丁,噩梦就来...

Win10如何彻底关闭wsappx进程(win10 wsappx怎么关闭)

win10如何彻底关闭wsappx进程?wsappx进程是什么?wsappx进程是Windows10系统的一部分,这个进程是WindowsStore和微软通用应用程序平台(UWP)的依赖进程。...

Windows环境黑客入侵应急与排查(黑客入侵电脑原理)

1文件分析1.1临时目录排查黑客往往可能将病毒放在临时目录(tmp/temp),或者将病毒相关文件释放到临时目录,因此需要检查临时目录是否存在异常文件。假设系统盘在C盘,则通常情况下的临时目录如下...

Windows 11 24H2 KB5044384出现大面积安装失败、任务管理器0进程等问题

Windows11KB5044384更新由于出现大量错误而无法在Windows1124H2上安装、其中包括一个奇怪的错误,即由于0x800f0922、0x800736b3和0x8...

取消回复欢迎 发表评论: