多层嵌套 if-else 代码优化重构的几种方案

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.优化思路

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

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

多层嵌套 if-else 代码优化重构的几种方案

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嵌套层数呈指数型增长,最终收敛于某次线上重大事故。

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

作者:MR_Peach07

来源:blog.csdn.net/MR_Peach07/article/

details/121590278

推荐

Java面试题宝典

技术内卷群,一起来学习!!

多层嵌套 if-else 代码优化重构的几种方案

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

原文始发于微信公众号(Java知音):多层嵌套 if-else 代码优化重构的几种方案

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/130340.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!