作者:何白白

cnblogs.com/hebaibai/p/11095590.html

这是在一次公司项目中进行重构时,一些复杂业务时想到的一个去掉一些if else的办法。能够使代码逻辑更加清晰,减少一些业务上的耦合。

业务说明

我所在的是一个做保险的项目组,这次重构是针对其中的保费计算和核保的业务。

项目重构之前,在保费计算的接口中,有大量的条件判断语句来判断这次进行保费计算的产品是哪一个,然后调用该产品的保费计算方法。代码大致看起来就是这个样子:

//产品编号
String product = "123123";
if (product.equals("11111")) {
} else if (product.equals("11111")) {//使用产品编号是11111的service类进行保费计算//
} else if (product.equals("22222")) {//使用产品编号是22222的service类进行保费计算//
} else {//执行其他的保费计算
}

这些通过一个编号进行判断并执行特定代码的方法在项目中到处都是,一旦添加了新的产品,或者产品在一些特定销售渠道中有特定的一些操作(比如有一些折扣啊什么的)就要在代码的各种关键地方添加一堆 if eles,大大影响了代码的可读性和可维护性。常常是修改了一处代码,影响到很多别的地方,造成一些看起来很奇怪的bug。

所以我在接手这个项目的时候想到了重构。希望在重构后能够使各个产品之间的代码没有关联,业务分明,相互不影响。

开始重构

分析业务,抽象出接口

重构代码之前要先分析一下业务,因为我的这个项目是做保费计算的,虽然有一大堆的判断产品编号的代码,但是最终它们做的都是同一件事情。这里可以把保费计算抽象成为一个接口,在接口中定义执行保费计算所需要的共同的方法。

大致上是这个样子:

/*** 保费计算接口** @author hjx*/
public interface PremiumCalculate {//保费计算Result calculate(Param param);//其他的保费计算中需要的方法。。。
}

这一步主要是梳理一下项目中的业务,并把业务中相同的步骤抽象出来。

根据不同的产品实现不同产品的保费计算

这一步就是实现上面的接口了,每个产品实现一遍接口。这里会存在一个问题,就是有很多产品在进行保费计算的时候,只有某几个步骤不一样。如果每个实现上都写一遍,会造成大量的重复代码。

我们可以建一个抽象类来实现这个接口,将大部分共有的实现代码写在抽象类中。就像这样:

/*** 抽象的保费计算*/
public abstract class AbstractPremiumCalculate implements PremiumCalculate {/*** 实现共有的一些代码* @param premiumInfo* @return*/@Overridepublic Result calculate(Param param) {//将共有的实现写在这里}
}

这样在新添加一个实现类的时候只要继承这个抽象类,重写其中的某些方法就可以了。这时我们可能有很多实现类,比如:

  • PremiumCalculateP001Impl.java

  • PremiumCalculateP002Impl.java

  • PremiumCalculateP003Impl.java

  • PremiumCalculateP004Impl.java

这时就可以在执行保费计算的时候根据不同的产品调用不同的实现。每个实现类只写一个产品的业务就行,类之间相互不影响。在新添加产品的时候也是只需要添加一个新的实现类就好了。

但是这样还有问题

但是这样还是有问题的,因为还是要在业务代码中写一堆的if else 来判断这次到底需要哪一个实现类来执行保费计算,这时可以写一个工厂类。根据传入的产品编号或者别的什么参数,返回特定的一个实现类,像是这样:

/*** 保费计算接口工厂类*/
@Service
public class PremiumCalculateFactory {@Autowired@Qualifier("premiumCalculateP0001")private PremiumCalculate premiumCalculateP0001;@Autowired@Qualifier("premiumCalculateP0002")private PremiumCalculate premiumCalculateP0002;@Autowired@Qualifier("premiumCalculateP0003")private PremiumCalculate premiumCalculateP0003;@Autowired@Qualifier("premiumCalculateP0004")private PremiumCalculate premiumCalculateP0004;。。。。其他的保费计算实现对象public PremiumCalculate getPremiumCalculate(String productCode) {if (productCode.equals("P0001")) {return premiumCalculateP0001;} else if () {//其他的条件下,返回其他的对象}}}

添加了工厂类之后,我们在获取保费计算对象的时候只需要调用getPremiumCalculate()方法就可以了,具体返回哪一个实现对象,就交给工厂类来处理。可以精简调用保费计算时的代码。

还是免不了写if else,改造PremiumCalculateFactory

在提供了工厂类之后,还是免不了写很多的条件判断,只不过是把所有的条件判断写在了一起。这时随着产品数量的增多,if else 也会不停地增多,维护起来依然费劲。工厂等设计模式,参考:设计模式内容聚合

这里spring容器就可以排上用场了。spring中有一个BeanFactory对象,也是一个工厂,我们可以用它来改造PremiumCalculateFactory。

首先我们在编写各个产品对应的保费计算实现类的时候都会将它注册进spring容器中,成为一个bean。我们需要给这个bean指定一个名称比如:

//在这里指定bean的名称
@Service("PremiumCalculate:"+产品编号)
public class PremiumCalculateP0001Impl implements PremiumCalculate {。。。
}

然后修改PremiumCalculateFactory

/*** 保费计算接口工厂类*/
@Service
public class PremiumCalculateFactory {@Autowiredprivate BeanFactory beanFactory;public PremiumCalculate getPremiumCalculate(String productCode) {Object bean = beanFactory.getBean("PremiumCalculate:" + productCode);if (bean instanceof PremiumCalculate) {return (PremiumCalculate) bean;}throw new UnsupportedOperationException("不支持的编号:" + productCode);}
}

这样,条件判断的步骤就可以省略了。

结果

在保费计算和核保项目经过这样重构后,每个产品的业务代码相互不关联,维护和添加产品时也能减少工作量,还是比较成功的。

不足

这样写会有一个比较大的问题,就是在产品数量增多的时候,java文件数量也会随之变多。但是目前的业务中产品数量还可以忍受。由于产品配置功能的出现,大部分产品都可以通过数据库配置出来。这里只是写配不出来的一部分,所以这种模式还是可行的。


Sudofx改进方法:

在PremiumCalculate中添加一个supportProduct,代码如下:

public interface PremiumCalculate {//保费计算Result calculate(Param param);//其他的保费计算中需要的方法。。。// 添加一个supportProductboolean supportProduct(String product);
}

在使用到的类中注入所有的PremiumCalculate:

@Autowired
private List<PremiumCalculate> calculates

最后遍历这个list通过supportProduct来判断。

更多优化代码结构,参考:

解锁新姿势:探讨复杂的 if-else 语句“优雅处理”的思路

还在业务中用if else,策略模式了解一下

减少该死的 if else 嵌套

END

Java面试题专栏

【51期】一道阿里面试题:说说你知道的关于BeanFactory和FactoryBean的区别

【52期】记一道简单的Java面试题,但答错率很高!

【53期】面试官:谈一下数据库分库分表之后,你是如何解决事务问题?

【54期】Java序列化三连问,是什么?为什么需要?如何实现?

【55期】面试中经常被问到Java引用类型原理,带你深入剖析

【56期】你说你熟悉并发编程,那么你说说Java锁有哪些种类,以及区别

【57期】面试官问,MySQL建索引需要遵循哪些原则呢?

【58期】盘点那些面试中最常问的MySQL问题,第一弹!

【59期】MySQL索引是如何提高查询效率的呢?(MySQL面试第二弹)

【60期】事务隔离级别中的可重复读能防幻读吗?(MySQL面试第三弹)

我知道你 “在看”

记一次项目代码重构:使用Spring容器干掉条件判断相关推荐

  1. 【报错笔记】在eclipse中做Spring项目时,创建Spring容器时老是出错

    在eclipse中做Spring项目时,创建Spring容器时老是出错 写完这句代码无法导包,最后发现包导错了,我原来导的4.3.9的包,而且是后缀为其他的包,而且对JDK也有要求,我又下载了5.0. ...

  2. 在python中要表示一个空的代码块可以使用空语句什么_Python条件判断语句if

    Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待 4999元包邮 去购买 > 程序在一般情况下是按顺序执行的,就像流水账一样,一条一条从上往下顺序 ...

  3. 项目中WebService使用Spring容器的配置

    <context:component-scan base-package="com.huawei.support">         <context:inclu ...

  4. 为什么要代码重构?如何重构?常见重构技巧,值得收藏!

    >>号外:关注"Java精选"公众号,回复"2021面试题",领取免费资料!"Java精选面试题"小程序,3000+ 道面试题在 ...

  5. 代码重构技巧宝典,学透本篇就足够了!

    本文来源:http://n5d.net/ma76k 关于重构 为什么要重构 1_代码重构漫画.jpeg 项目在不断演进过程中,代码不停地在堆砌.如果没有人为代码的质量负责,代码总是会往越来越混乱的方向 ...

  6. spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)

    原文地址:http://zhaoshijie.iteye.com/blog/1974682 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他 ...

  7. Spring 容器:三种方式解决 Resource leak: ‘applicationContext‘ is never closed 问题

    文章目录 前言 一.Spring 容器警告的产生 1.1.项目场景 二.Spring 容器未关闭后果分析 2.1.肉眼可见的警告 2.2.导致的内存泄漏 2.2.1.什么是内存泄漏? 2.2.2.如何 ...

  8. 当Spring 容器初始化完成后执行某个方法

    当Spring 容器初始化完成后执行某个方法 实现ApplicationListener 使用注解:`@PostConstruct` 实现ApplicationListener 在做web项目开发中, ...

  9. Spring容器,控制反转,依赖注入

    Spring boot学习之旅,为更好督促自己学习以记之,仅供参考. spring容器 程序启动的时候会创建spring容器,扫描给spring容器一个清单,比如:@Controller, @Bean ...

最新文章

  1. 大作完成了一部分,陆续往上放吧
  2. vue --- 2.0数据的响应式的一种实现
  3. 作为后端开发如何设计数据库系列文章(一)设计传统系统表结构
  4. lisp 多边形象限_AutoLISP图程序设计.ppt
  5. r语言中paste函数_R中的paste()函数-简要指南
  6. 用maya怎么做ak47_串串香应该怎么用配料才能做得好吃
  7. Redhat/Ubuntu/Windows下安装Docker
  8. 2.1 InnoDB存储引擎(概述、版本、体系结构)
  9. 上海迪士尼度假区快乐旅游趋势报告:中国主题乐园行业八大趋势
  10. 推荐一款可自创外星文字的工具
  11. SpringBoot整合EasyExcel实现Excel表格的导出功能
  12. 重温经典,续写传奇,迈巴赫S600改铱银色加铁灰色双拼喷漆
  13. 微生物组-扩增子16S分析和可视化(线上/线下,本周开课,2021.10)
  14. Linux文件权限与目录配置
  15. .md文件转.pdf文件
  16. 深圳首辆数字人民币主题观光巴士亮相
  17. find和findstr区别
  18. matlab 电力电子元件对应名称,MATLAB在电力电子技术的应用
  19. 带领域变异的多模态优化差分进化算法(DE/NCDE/NSDE/)
  20. 【Vue3.0 + Element-plus】el-tree树状结构节点前箭头样式修改

热门文章

  1. 三星Galaxy Fold入网:屏幕故障+数次跳票 热度还有多少?
  2. 放弃耳机孔、放弃按键的手机我们是怎么接受并习惯的?
  3. 大家一起看广告?微信朋友圈广告@好友评论互动功能全量开放
  4. 马云妇女节寄语女性:没有败家的女人 只有爱家的女人
  5. 深圳出差 第一天【原创】
  6. html canvas 画注释框
  7. Qt实践录:常见控件操作示例2
  8. Windows下的Makefile
  9. [Linux网络编程]以太网封装格式及相关结构体
  10. Eclipse安装UML 插件