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

news/2024/7/8 12:29:17 标签: 策略模式, Handle, 面试, 设计模式, 代码优化

一.前言

        最近项目有实习的同事加入,很多实习同事反映,看不懂项目中的一些使用了设计模式的代码,比如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 {
	
	@Override
	public void execut(Object sheetData) {
		// TODO:  真实的业务逻辑  
	}

	@Override
	public String getSheetName() {
		return "Bidding";
	}
}
@Service
public class InviteTendersSheetService implements ISheetService {

	@Override
	public void execut(Object sheetData) {
		// TODO:  真实的业务逻辑
	}

	@Override
	public String getSheetName() {
		return "Invite";
	}
}

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

@Service
public class ExcelSheetHandleService {

	@Autowired
	private List<ISheetService> sheetServices;

	@Async
	public 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方法。

	@Autowired
	private 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.niftyadmin.cn/n/5537179.html

相关文章

【python】使用conda管理python项目:conda管理不同项目环境,pip下载最新的包

文章目录 一. python包管理概述1. miniforge、Miniconda与Anaconda2. conda与pip的区别是什么&#xff1f;3. pip与conda配合使用 二. 使用conda管理不同py环境1. 创建一个环境2. 解决冲突 三. 命令合集1. conda1.1. 常用1.2. 环境管理1.3. conda分享环境与pip生成requirements…

基于单片机技术的按键扫描电路分析

摘 要&#xff1a; 单片机应用技术被广泛应用于各种智能控制系统中&#xff0c;是电子信息类专业学生必修的一门专业课。在单片机端口信息输入模块中&#xff0c;按键是主要元器件之一&#xff0c;笔者主要介绍矩阵键盘的电路设计及控制程序编写&#xff0c;分析了单片机端口连…

Static关键字的用法详解

Static关键字的用法详解 1、Static修饰内部类2、Static修饰方法3、Static修饰变量4、Static修饰代码块5、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java编程语言中&#xff0c;static是一个关键字&#xff0c;它可以用于多种上…

智慧生活新篇章,Vatee万腾平台领航前行

在21世纪的科技浪潮中&#xff0c;智慧生活已不再是一个遥远的梦想&#xff0c;而是正逐步成为我们日常生活的现实。从智能家居的温馨便捷&#xff0c;到智慧城市的高效运转&#xff0c;科技的每一次进步都在为我们的生活增添新的色彩。而在这场智慧生活的变革中&#xff0c;Va…

转型AI产品经理前需要搞懂的9个问题

笔者近10年来一直工作于AI领域技术和产品一线&#xff0c;今天分享9个大家普遍关注却少有人系统回答的问题。 1.下一个10年&#xff0c;AI产业在国内的发展和职业发展的机遇如何&#xff1f; 从四方面看&#xff1a; &#xff08;1&#xff09;技术角度&#xff1a;逐步成熟…

Spring源码十:BeanPostProcess

上一篇Spring源码九&#xff1a;BeanFactoryPostProcessor&#xff0c;我们看到ApplicationContext容器通过refresh方法中的postProcessBeanFactory方法和BeanFactoryPostProcessor类提供预留扩展点&#xff0c;他可以在Spring容器的层面对BeanFactroy或其他属性进行修改&#…

加速度传感器信号处理注意事项

1 传感器分类 对于压电式压力传感器而言&#xff0c;输出信号是最重要的选择标准之一。压电式压力传感器与电子电路相连&#xff0c;电子电路将传感器产生的电荷成比例转换为电压。 如果选用外部设备&#xff08;电荷放大器&#xff09;充当电子元件&#xff0c;则称其为电…

【vue】JSON数据导出excel

前言 导出方式有很多种&#xff0c;但是若只需要数据导出成.xlsx文件并下载的话&#xff0c;只用xlsx一个插件就行 目标 1 实现数据导出excel 2 如何设置表格列宽 3 如何在文件中创建工作表 准备工作 1 安装 npm i xlsx -S 2 引入 npm i xlsx -S 二、导出excel 创建文件 con…