相信大夥兒都見過這樣的代碼:
if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
if (true) {
// do something
}
}
}
}
}
功能跑起來沒問題,但是作為一名追求代碼精簡的程式員,能用一行代碼完成功能絕不寫三行。業務開發過程中正好也遇到這樣的重構訴求,于是有了這篇重構過程複現和衍生思考部落格。
結論先行,重構這類嵌套if-else的代碼,我主要采取了以下三種方式:
- 三目運算符
- 方法分層
- 多态
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代碼的重構,比如用一些特殊的資料結構去或者設計模型思想進行,如:
- 使用Map替代分支語句,把所有狀态類型預先緩存在Map裡,需要調用的時候直接get擷取具體狀态類型,以此消除分支;
- 使用衛語句,優化代碼順序,減少else條件的使用;
- 設計模式中政策模式和狀态模式,也可以用于減少業務接口的嵌套if-else代碼,但歸根到底還是用到了多态的特性去進行抽象。
3.反思
重構前的代碼,也是能跑通的,并且思路也是清晰的,但是為什麼還要花力氣去重構他呢?這個問題這就是重構需要解決的問題之一。
重構前的代碼在目前需求下是能夠勝任工作的,但是一個月後,或者一年之後,甚至三五年後,這份代碼就會因為曆任産品的不同需求無限膨脹,一點也不”開發-閉合“,并且維護難度系數和if-else嵌套層數呈指數型增長,最終收斂于某次線上重大事故。
重構後的代碼就會減少這樣情況的發生。是以寫下這篇部落格,一是對此次重構過程複盤,二是提煉此類問題的重構模型,友善以後查閱補充。
來源:blog.csdn.net/MR_Peach07/article/details/121590278