天天看點

多層嵌套 if-else 代碼優化重構的幾種方案

作者:老誠不bug

相信大夥兒都見過這樣的代碼:

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嵌套層數呈指數型增長,最終收斂于某次線上重大事故。

重構後的代碼就會減少這樣情況的發生。是以寫下這篇部落格,一是對此次重構過程複盤,二是提煉此類問題的重構模型,友善以後查閱補充。

來源:blog.csdn.net/MR_Peach07/article/details/121590278

繼續閱讀