⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/MR_Peach07/article/details/121590278 「MR_Peach07」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

1.背景

相信大伙儿都见过这样的代码:

if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
}
}
}
}
}

功能跑起来没问题,但是作为一名追求代码精简的程序员,能用一行代码完成功能绝不写三行。业务开发过程中正好也遇到这样的重构诉求,于是有了这篇重构过程复现和衍生思考博客。

结论先行,重构这类嵌套if-else的代码,我主要采取了以下三种方式:

  1. 三目运算符
  2. 方法分层
  3. 多态

2.优化思路

下面我将以实例的方式呈现这个重构过程。

重构前的业务代码是长这样的:

2.1.三目运算符

利用三目运算符对一些简单if-else进行优化。

就像这样:

不使用三目运算符:

if (queryParam.getCreateStartTime() != 0) {
String strStartDate = DateUtils.longToFormatDate(queryParam.getCreateStartTime());
}

使用三目运算符:

String strStartDate = !(queryParam.getCreateStartTime() == 0 || queryParam.getCreateEndTime() == 0) ? DateUtils.longToFormatDate(queryParam.getCreateStartTime()) : null;

2.2.方法分层

所谓方法分层是受到设计模式里面的单一职责原则(SRP)启发,代码中很多参数需要判空。这时候抽象一个方法出来进行所有的参数合法性校验,以此达到代码简化的目的。

业务代码只进行业务逻辑编写,参数校验统一由参数校验方法完成。

就像这样:

@Override
public void checkListQueryParams(ListQueryParam queryParam) {
if (Objects.isNull(queryParam.getPageNo()) || Objects.isNull(queryParam.getPageSize())) {
throw new ChartException(MsgCode.CONSTRAINT_VIOLATION, PAGE_PARAMS_NULL);
}
}

业务侧直接调用该参数校验方法即可。

checkParamsService.checkListQueryParams(params);

2.3.多态

上述两种方法都是局部优化if-else代码结构,如若要整体减少业务代码中的嵌套if-else,一个思路就是利用多态的特性,每种业务单独处理,在接口不再做任何业务判断。把业务代码(这里即是指条件查询方法)抽象出来,作为基础类,然后针对每种业务各自实现其子类。

具体例子如下:

BackgroundConditionQuery.java

public abstract class BackgroundConditionQuery<T> {

public abstract List<T> doQuery(ListQueryParam queryParam, ConditionQueryParams conditionQueryParams);
}

BackgroundChartListConditionQuery.java

public class BackgroundChartListConditionQuery extends BackgroundConditionQuery {

@Autowired
private ChartDao chartDao;

@Override
public List<Chart> doQuery(ListQueryParam queryParam, ConditionQueryParams conditionQueryParams) {
List<Chart> chartList = new ArrayList<>();
if (!Objects.isNull(queryParam.getSceneStatus())) {
if (queryParam.getSceneStatus().equals(CHECK_STATUS_ALL)) {
chartList = queryWithoutSceneStatus(conditionQueryParams);
} else {
byte sceneStatus = queryParam.getSceneStatus();
chartList = chartDao.selectByConditionWithSceneStatus(conditionQueryParams.getStrStartDate()
, conditionQueryParams.getStrEndDate(), conditionQueryParams.getId()
, conditionQueryParams.getChartName(), conditionQueryParams.getCreatorId(), sceneStatus);
}
} else {
chartList = queryWithoutSceneStatus(conditionQueryParams);
}
return chartList;
}

private List<Chart> queryWithoutSceneStatus(ConditionQueryParams conditionQueryParams) {
return chartDao.selectByConditionWithAll(conditionQueryParams.getStrStartDate()
, conditionQueryParams.getStrEndDate(), conditionQueryParams.getId()
, conditionQueryParams.getChartName(), conditionQueryParams.getCreatorId());
}
}

BackgroundTemplateListConditionQuery.java

public class BackgroundTemplateListConditionQuery extends BackgroundConditionQuery {

@Autowired
private TemplateDao templateDao;

@Override
public List<Template> doQuery(ListQueryParam queryParam, ConditionQueryParams conditionQueryParams) {
List<Template> templateList = new ArrayList<>();
if (!Objects.isNull(queryParam.getApproveStatus())) {
if (queryParam.getApproveStatus().equals(CHECK_STATUS_ALL)) {
templateList = queryWithoutApproveStatus(conditionQueryParams);
} else {
byte approveStatus = queryParam.getSceneStatus();
templateList = templateDao.selectByConditionWithApproveStatus(conditionQueryParams.getStrStartDate()
, conditionQueryParams.getStrEndDate(), conditionQueryParams.getId()
, conditionQueryParams.getChartName(), conditionQueryParams.getCreatorId(), approveStatus);
}
} else {
templateList = queryWithoutApproveStatus(conditionQueryParams);
}
return templateList;
}

private List<Template> queryWithoutApproveStatus(ConditionQueryParams conditionQueryParams) {
return templateDao.selectByConditionWithAll(conditionQueryParams.getStrStartDate()
, conditionQueryParams.getStrEndDate(), conditionQueryParams.getId()
, conditionQueryParams.getChartName(), conditionQueryParams.getCreatorId());
}
}

这样之后,业务接口代码就只需要进行调用工作,完成了大量的嵌套if-else代码的重构。最后,业务接口代码就像这样:

List<Chart> chartList = new ArrayList<>();
// 时间日期筛选
String strStartDate = !(queryParam.getCreateStartTime() == 0 || queryParam.getCreateEndTime() == 0) ? DateUtils.longToFormatDate(queryParam.getCreateStartTime()) : null;
String strEndDate = !(queryParam.getCreateStartTime() == 0 || queryParam.getCreateEndTime() == 0) ? DateUtils.longToFormatDate(queryParam.getCreateEndTime()) : null;
// 其他筛选条件
String id = StringUtils.equals(SEARCH_TYPE_TEMPLATE_Id, queryParam.getSearchType()) ? queryParam.getSearchValue() : null;
String chartName = StringUtils.equals(SEARCH_TYPE_TEMPLATE_NAME, queryParam.getSearchType()) ? queryParam.getSearchValue() : null;
String creatorId = StringUtils.equals(SEARCH_TYPE_CREATOR_Id, queryParam.getSearchType()) ? queryParam.getSearchValue() : null;
ConditionQueryParams conditionQueryParams = new ConditionQueryParams(strStartDate, strEndDate, id, chartName, creatorId);
chartList = backgroundChartListConditionQuery.doQuery(queryParam, conditionQueryParams);

2.4.其他方法

除了以上方法外,还有两种方法可以进行嵌套if-else代码的重构,比如用一些特殊的数据结构去或者设计模型思想进行,如:

  1. 使用Map替代分支语句,把所有状态类型预先缓存在Map里,需要调用的时候直接get获取具体状态类型,以此消除分支;
  2. 使用卫语句,优化代码顺序,减少else条件的使用;
  3. 设计模式中策略模式和状态模式,也可以用于减少业务接口的嵌套if-else代码,但归根到底还是用到了多态的特性去进行抽象。

3.反思

重构前的代码,也是能跑通的,并且思路也是清晰的,但是为什么还要花力气去重构他呢?这个问题这就是重构需要解决的问题之一。

重构前的代码在当前需求下是能够胜任工作的,但是一个月后,或者一年之后,甚至三五年后,这份代码就会因为历任产品的不同需求无限膨胀,一点也不”开发-闭合“,并且维护难度系数和if-else嵌套层数呈指数型增长,最终收敛于某次线上重大事故。

重构后的代码就会减少这样情况的发生。因此写下这篇博客,一是对此次重构过程复盘,二是提炼此类问题的重构模型,方便以后查阅补充。

文章目录
  1. 1. 1.背景
  2. 2. 2.优化思路
    1. 2.1. 2.1.三目运算符
    2. 2.2. 2.2.方法分层
    3. 2.3. 2.3.多态
    4. 2.4. 2.4.其他方法
  3. 3. 3.反思