代码技巧专题 -- 使用策略模式编写HandleService

ops/2024/10/20 6:41:22/

一.前言

        最近项目有实习的同事加入,很多实习同事反映,看不懂项目中的一些使用了设计模式的代码,比如HandleService,Chains,Listener等。本篇就介绍一下策略模式在项目中的使用,也就是我们常在项目中看到的XXXHandleService。

二.业务场景描述

        假如有一个业务场景,我们要导入一个excel文件,excel文件有多个sheet页,每个sheet页的数据对应一套处理逻辑(比如sheet页A的数据解析完在数据库的A表进行存储,sheet页B的数据解析完成后要存到mongodb),每个sheet页的处理逻辑由一个开发同事进行开发。

三.代码

        1.定义一个接口,接口约定两个方法

public interface ISheetService {/*** 执行方法,入参的sheetData代表一个sheet页中的数据* @param sheetData*/void execut(Object sheetData);/*** 要求每个实现 ISheetService 的实现类都有一个 获取sheetname的方法* @return*/String getSheetName();
}

       2.两个业务实现类,他们都实现了上面定义的接口。且getSheetName方法各自返回该业务类要处理的sheet名称。

@Service
public class BiddingService implements ISheetService {@Overridepublic void execut(Object sheetData) {// TODO:  真实的业务逻辑  }@Overridepublic String getSheetName() {return "Bidding";}
}
@Service
public class InviteTendersSheetService implements ISheetService {@Overridepublic void execut(Object sheetData) {// TODO:  真实的业务逻辑}@Overridepublic String getSheetName() {return "Invite";}
}

        3.编写一个 ExcelSheetHandleService ,作为管理业务类的地方

@Service
public class ExcelSheetHandleService {@Autowiredprivate List<ISheetService> sheetServices;@Asyncpublic void analysisSheetData(Map<String, Object> sheetName2Sheet) {sheetName2Sheet.entrySet().forEach(entry->{String sheetName = entry.getKey();Object sheetData = entry.getValue();sheetServices.stream().filter(service->service.getSheetName().equals(sheetName)).findFirst().ifPresent(iSheetService -> iSheetService.execut(sheetData));});}
}

        4.controller层:

        controller层作为入口层,做了两件事,第一件事是将excel文件解析成了Map<sheetName, sheetData> 的格式,第二件事是调用 excelSheetHandleService的analysisSheetData方法。

	@Autowiredprivate ExcelSheetHandleService excelSheetHandleService;@PostMapping("/uploadExcel")public R uploadExcel(@RequestParam("file") MultipartFile file) {// 解析excel 将多sheet页的excel解析成 key: sheetName  value -> sheet页数据  的格式Map<String, Object> sheetName2Data = convertExcelData(file);if (!CollectionUtils.isEmpty(sheetName2Data)) {excelSheetHandleService.analysisSheetData(sheetName2Data);}return R.ok();}

四. 代码讲解

        1.代码运行流程

        前端调用文件上传后,首先controller层中,将excel文件根据sheet解析成Map<String, Object>的格式,map的key是sheet页的名称,map的value是该sheet页的数据。

        excel文件解析完成后,调用ExcelSheetHandleService 的执行方法。

        再看ExcelSheetHandleService ,首先注意该类的属性,有一个List<ISheetService>,并且在上面加了@Autowired属性,这里其实是一个spring的特性,spring允许我们注入一个Collection,比如List<interface>,Map<class, Service>。比如我们这里的这个List<ISheetService>,其实就是让spring帮我们把所有实现了ISheetService这个接口的实现类都注入到这里了。

        接着我们看一下ISheetService及它的两个实现类,ISheetService规定了实现类必须实现getSheetName这个方法,然后它的两个实现类分别实现这个方法,并返回一个字符串,该字符串和excel文件中的sheet名是一致的。

        回到ExcelSheetHandleService 的 analysisSheetData方法,在方法中,我们利用java8的stream来遍历了我们注入的ISheetService的List,调用每一个ISheetService实现类的getSheetName方法,如果sheet名称和getSheetName的返回值一致,则认为该实现类就是用于处理该sheet的Service,然后再调用该service的真实业务执行方法execut。

        五.这样写代码的好处

        大家理解上述代码后会发现,其实以上代码完全可以用if else 或者 switch case完成,比如这样:

switch(sheetName) {case "sheetNameA":functionA();break;case "sheetNameB":functionB();break;case DEFAULT;throw new Exception("非法sheetName");
}

然后在functionA,functionB中写自己的逻辑。而且,这样写似乎还更好理解,既然如此,为何我们还要使用策略模式HandleService,来让代码变得很复杂呐?

        其实,在写代码时,除了完成需求,保障性能之外,我们还需要考虑很多其他的问题,诸如代码的可维护性,团队的合作。拿本篇举例的这个业务场景来说,当前需求是解析excel,读两个sheet页,那么之后很可能拓展成三个,四个sheet页。在代码拓展时,增加更多的case条件和function在一个service中,会让这个类文件越来越庞大,难以维护,而写一个新的service,既不侵入老代码,后面代码维护也方便。再说团队开发问题,当前我们是三个人做这个需求,如果大家都在一个java类文件中通过switch case来写代码,然后提交都在这一个文件,很容易出现冲突,导致不小心覆盖掉代码之类的问题,而每一个人写自己的service,则可以规避这种问题。


http://www.ppmy.cn/ops/56282.html

相关文章

向新求质 智赋广西,2024华为数智转型助力企业高质量发展论坛在南宁举办

7月5日以“向新求质 智赋广西”为主题的2024华为数智转型助力企业高质量发展论坛在南宁成功举办。来自广西区管企业、驻桂央企和国有企业等80余位中高层管理者&#xff0c;与华为业务变革专家、数字化转型专家共同探讨企业数字化转型新路径&#xff0c;为企业创新转型发展献计献…

VPN 的入门介绍

VPN&#xff08;虚拟专用网络&#xff09; 简介 虚拟专用网络&#xff0c;简称虚拟专网&#xff08;VPN&#xff09;&#xff0c;其主要功能是在公用网络上建立专用网络&#xff0c;进行加密通讯。在企业网络中有广泛应用。VPN网关通过对数据包的加密和数据包目标地址的转换实…

c++入门基础篇(上)

目录 前言&#xff1a; 1.c&#xff0b;&#xff0b;的第一个程序 2.命名空间 2.1 namespace的定义 2.2 命名空间使用 3.c&#xff0b;&#xff0b;输入&输出 4.缺省参数 5.函数重载 前言&#xff1a; 我们在之前学完了c语言的大部分语法知识&#xff0c;是不是意…

数据结构第10节:平衡树

平衡树是一种特殊的二叉搜索树&#xff0c;它的设计目的是为了保持树的平衡&#xff0c;从而保证所有操作的时间复杂度保持在O(log n)&#xff0c;即使在最坏的情况下也是如此。最常见的平衡树之一是AVL树&#xff0c;它是以发明者G.M. Adelson-Velsky和E.M. Landis的名字命名的…

ESP32 步进电机精准控制:打造高精度 DIY 写字机器人,实现流畅书写体验

摘要: 想让你的 ESP32 不再仅仅是控制灯光的工具吗&#xff1f; 本文将带你使用 ESP32 开发板、步进电机和简单的机械结构打造一个能够自动写字的机器人。我们将深入浅出地讲解硬件连接、软件代码以及控制逻辑&#xff0c;并提供完整的项目代码和电路图&#xff0c;即使是 Ardu…

C#中,不同命名空间下面完全相同的类对象进行赋值

背景前提&#xff1a; 1、在命名空间ModelA、ModelB下&#xff0c;都有完全相同的类定义ClassX、ClassY、ClassZ &#xff1b; 2、ClassBase是父类&#xff0c;它的子类有&#xff1a;ClassX、ClassY、ClassZ 3、在ModelB下不能访问ModelA&#xff1b; 4、有大量文件&…

ComfyUI入门教程

本文主要介绍了通过源码运行comfyui&#xff0c;默认例子介绍&#xff0c;节点管理器的使用&#xff0c;以及界面汉化。可多参考开源工作流&#xff0c;多加实践&#xff0c;从而掌握comfyui操作。 1.源码运行comfyui 执行命令python main.py如下&#xff1a; 安装numpy 1.x最…

解析java128陷阱

一、提要 在java开发时&#xff0c;由于基本类型不能调用方法&#xff0c;在某些方面很不方便&#xff0c;因此产生了包装类。我们把基本类型和对应的包装类的转换叫装箱、拆箱。 1.装箱 基本类型转成包装类对象 关键字valueOf->装箱,可以指定进制&#xff1a; Integer…