写点什么

使用策略模式优化代码实践,如何让项目快速起飞,冒泡排序 java 代码视频

作者:MySQL神话
  • 2021 年 11 月 28 日
  • 本文字数:2644 字

    阅读完需:约 9 分钟

由于用户的类型比较多,所以当我接手的时候已经有 8 个 if-esle 了,由于这个项目会逐步的跟其他平台对接,要同步的用户类型会越来越多,而且也不能排除什么时候不新增,反而要取消一部分类型的同步情况。


就这个情况来说,一方面每一次新增类型都会让 if-else 串越来越长,取消一些类型的同步还要直接删除 if-else 里的对应代码;另一方面,这个业务的需求相对稳定,同步方法会不一样,但是一定会根据类型来判断。出于以上考虑,我决定趁现在牵扯范围不大的时候重构一下。


二、思路



1.抽取策略接口和策略类

首先,由于每种用户类型的同步方法是由各模块自己提供的,其实已经抽出了策略,只是没有实现一个统一的策略接口。


但是我在这一步遇上了问题:


  • 各模块的同步方法的名称不全部一样;

  • 由于年代久远,旧代码是不允许改的。


代码不让改,就没法通过为旧实现类新增接口实现多态,方法名不一样,那么反射这条路子也走不通。我想到了装饰器,为每个实现类新增一个装饰器类,注册的时候通过装饰器去调用同步方法,但是这样缺点很明显,会引入一个装饰器接口+n 多个装饰器类,为了优化这一个方法,反而要引入十几个类,实在是脱裤子放屁。


但是好在天无绝人之路,他们并不是完全没有相同点:


  • 虽然参数名不一样,但是每个同步方法都需要的参数数量和类型都是一样的;

  • 他们都返回一个布尔值


这让我想起了 JDK8 的函数式接口,将策略接口改造为函数式接口,由于同步方法的参数和返回值类型都是一样的,就可以直接以 Lambda 表达式的形式将各个模块的同步方法放进去,这样就不需要改动模块的代码了。


新增的接口如下:


@FunctionalInterface


public interface IUserSynchronizeSerivice {


/**


  • 同步方法


*/


public boolean sync(String userId, String projectId, String declareId);


}

2.策略池的实现

接着,为了实现原本 if-else 的逻辑,我需要一个策略池,能够建立起一个用户类型跟对应的同步策略的映射关系,一开始,我打算直接写在?register()方法所在的类中加入以下代码:


@Autowired


private AUserService aUserService;


@Autowired


private BUserService bUserService;


private static final Map<String, UserSynchronizeTyeEnum.IUserSynchronizeService> synchronizeServiceStrategy = new HashMap<>();


@PostConstruct


private void strategyInit(){


// spring 容器启动后将策略装入策略池


synchronizeServiceStrategy.put(UserSynchronizeTyeEnum.A.type, aUserService::synchronization);


synchronizeServiceStrategy.put(UserSynchronizeTyeEnum.B.type, bUserService::sync);


}


但是这样在添加新的用户类型时,需要先去枚举类添加新枚举,然后再回到register()所在的类为策略池添加策略,这个两个逻辑上相连的过程被分散到了两个地方,而且仍然要修改register()所在类的代码。所以决定不用上述的代码,而是去对枚举类下手。


原本的枚举类是这样的:


/**


  • 老系统用户注册,用户类型与同步方法的枚举类


*/


public enum UserSynchronizeTyeEnum {


/**


  • 类型 A 的用户


*/


A("a"),


/**


  • 类型 B 的用户


*/


B("b");


/**


  • 用户类型


*/


private final String type;


UserSynchronizeTyeEnum(String type) {


this.type = type;


}


public String getType() {


return type;


}


}


为了保证逻辑能够集中,我决定将添加策略这一过程一起放到到枚举类里,在添加枚举的时候就把策略一起放进去:


注:下文的 SpringUtils 实现了 BeanFactoryPostProcessor 接口,是一个用于从 ConfigurableListableBeanFactory 获取对象的工具类。


/**


  • 老系统用户注册,用户类型与同步方法的枚举类

  • 添加新类型时,需要将模块对应的同步方法一并放入


*/


public enum UserSynchronizeTyeEnum {


/**


  • 类型 A 的用户


*/


A("a", (userId, projectId, declareId) -> {


return SpringUtils.getBean(AUserService.class).synchronization(userId, projectId, declareId);


}),


/**


  • 类型 B 的用户


*/


B("b", (userId, projectId, declareId) -> {


return SpringUtils.getBean(BUserService.class).sync(userId, projectId, declareId);


});


/**


  • 用户类型


*/


private final String type;


/**


  • 同步方法


*/


private final IUserSynchronizeService synchronizeService;


UserSynchronizeTyeEnum(String type, IUserSynchronizeService synchronizeService) {


this.type = type;


this.synchronizeService = synchronizeService;


}


}


由于由于枚举类已经相当于之前策略池的 Map 集合了,所以我们直接在里面添加一个?getSynchronizeService()方法,用于直接获取同步方法:


/**


  • 根据枚举值获取对应同步方法


*/


public Optional<IUserSynchronizeService> getSynchronizeService(String type) {


for (UserSynchronizeTyeEnum tyeEnum : UserSynchronizeTyeEnum.values()) {


if (tyeEnum.type.equals(type)) {


return Optional.of(tyeEnum.synchronizeService);


}


}


return Optional.empty();


}


到目前为止,策略池已经基本完成了,但是我们不难发现,现在为策略接口添加实现的地方也变成了枚举类中,策略接口?IUserSynchronizeService?一般也不会被用在其他地方,因此不妨把策略接口也一并引入枚举类中,让他成为一个枚举类的内部接口


现在,枚举类是这样的:



策略模式的枚举类


枚举类堆外只暴露根据类型获取方法的IUserSynchronizeService()?方法,以及 A 和 B 两个枚举。


完整的?UserSynchronizeTyeEnum枚举类代码如下:


/**


  • 老系统用户注册,用户类型与同步方法的枚举类

  • 添加新类型时,需要将模块对


《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享


应的同步方法一并放入。待用户注册时,会遍历枚举对象并根据类型获取对应的同步方法执行。


*/


public enum UserSynchronizeTyeEnum {


/**


  • 类型 A 的用户


*/


A("a", (userId, projectId, declareId) -> {


return SpringUtils.getBean(AUserService.class).synchronization(userId, projectId, declareId);


}),


/**


  • 类型 B 的用户


*/


B("b", (userId, projectId, declareId) -> {


return SpringUtils.getBean(BUserService.class).sync(userId, projectId, declareId);


});


/**


  • 用户类型


*/


public String type;


/**


  • 同步方法

最后

本人也收藏了一份 Java 面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们:


目录:



Java 面试核心知识点


一共有 30 个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!



Java 面试核心知识点


本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

用户头像

MySQL神话

关注

还未添加个人签名 2021.11.12 加入

还未添加个人简介

评论

发布
暂无评论
使用策略模式优化代码实践,如何让项目快速起飞,冒泡排序java代码视频