聊聊java中一些減少if else 的編碼習慣
前言
前段時間在閱讀別人所寫的程式碼的時候,發現其中一些業務相關的方法體內,出現了比較多的if-else
語句多層巢狀的情況 . 首先我個人不是不提倡寫if-else
語句,不得不說,很多時候,在寫某些邏輯 使用if-else
去做判斷,程式碼看起來還是十分直觀的,但是如果濫用if-else
,形成多層巢狀或者形成,其中每個case 還包含了大量的邏輯,此時從可讀性來說,使用if-else
就有點得不償失了 . 而且某些時候,可能並不需這麼多的if-else
,或者是可以使用其他編碼方式從而達到減少的if-else
的效果 .
減少if-else
的使用的方式有很多,例如設計模式層面的策略模式或者是責任鏈模式
if-else
使用 . 由於本人只是一個小菜鳥,如果有寫得不對的地方,懇請批評指正 .
一些減少if-else的編碼方式
方式一 : 提前return
首先展示一段程式碼示例 :
if (condition1) {
if (condition2) {
return getSomething();
} else {
return 0;
}
} else {
return 0;
}
複製程式碼
修改後的程式碼如下 :
//這裡最好對這個flag所判斷的邏輯補充註釋進行描述
boolean flag = !condition1 || (condition1 && !condition2)
if(flag) {
return 0;
}
if (condition1 && condition2) {
return getSomething();
}
複製程式碼
如果存在已知在某些條件下,需要返回固定值的邏輯,可以將這部分邏輯抽取為一個獨立的 if-else block
,並置於其他if-else block
的前面,當符合該特定條件時,直接提前 return
固定值 . 這種方式最直接的效果就是降低if-else
的巢狀數量 .
方式二 : 使用三目運運算元
先上例子,這裡以一個業務場景為例 :
查詢某條評論的圖片URL列表 (如果有,評論的圖片url列表以JSON陣列字串格式儲存在評論表中)
修改前的程式碼如下 :
Comment comment = getById(commentId);
if (Objects.isNull(comment)) {
throw new RuntimeException("評論不存在或已被刪除");
}
String imgListStr = comment.getImgList();
if(StringUtils.isEmpty(imgListStr)) {
return null;
}
return JSON.parseArray(imgListStr,String.class);
複製程式碼
修改後 :
Comment comment = getById(commentId);
if (Objects.isNull(comment)) {
throw new RuntimeException("評論不存在或已被刪除");
}
String imgListStr = comment.getImgList();
return StringUtils.isEmpty(imgListStr)) ?
null : JSON.parseArray(imgListStr,String.class);
複製程式碼
方式三 : 使用Assert斷言
在編寫業務程式碼過程中,如果需要對某些特定的條件進行判斷,且當條件不滿足時需要丟擲異常 . 對於這種場景,除了使用上面三目運運算元的示例當中的if
方式,還可以通過使用Spring Framework 給我們提供的 Assert
工具類進行 .
其中常用的API 有 :
-
isTrue(boolean expression,String message) : 當
expressio == false
時,會丟擲異常,異常的message
則為第二個入參 ; -
void notNull(@Nullable Object object,String message) : 同上,當
object == null
時,會丟擲異常; -
void notEmpty(@Nullable Collection<?> collection,當集合物件為
null
或者集合元素為空時,會丟擲異常 . - .....
還有其他較多方法,可以直接看原始碼的解析,當然實際上isTrue()
已經夠用了,如果需要更加的語義化,可以使用對應的API .
修改前程式碼 :
if (Objects.isNull(comment)) {
throw new RuntimeException("評論不存在或已被刪除");
}
複製程式碼
修改後程式碼 :
Assert.isTrue(Objects.nonNull(comment),"評論不存在或已被刪除");
Assert.notNull(comment,"評論不存在或已被刪除");
複製程式碼
目前Assert工具方法只能丟擲單一一種異常 java.lang.IllegalArgumentException
,如果需要自定義所丟擲的異常,則該方式不適用 .
方式四 : 使用Optional
Optional是 java8 的新特性,相當於一個物件的容器,主要用於物件的null
值校驗,以及在進行校驗後可鏈式地進行後續操作,如 : 丟擲異常、null
替換 等 .
其中我個人比較常用的幾個方法為 :
- static <T> Optional<T> ofNullable(T value) : 使用Optional 將物件進行包裹 ;
-
T orElse(T other) : Optional中的物件為
null
時,返回入參的物件 . -
T orElseGet(Supplier<? extends T> other) : Optional中的物件為
null
時,返回Supplier
提供的值 ; -
T orElseThrow(Supplier<? extends X> exceptionSupplier) : Optional中的物件為
null
時,丟擲supplier
提供的自定義異常
程式碼示例 :
Message message1 = Optional.ofNullable(getById(messageId))
.orElseThrow(() -> new RuntimeException("訊息不存在!"));
Message message2 = Optional.ofNullable(getById(messageId))
.orElse(new Message());
Message message3 = Optional.ofNullable(getById(messageId))
.orElseGet(Message::new);
複製程式碼
由於我日常需要的進行空值判斷的比較多的場景是從資料庫查詢資料完畢時,需要查詢結果進行空值判斷 . 由於我所在的公司使用的持久層框架是mybatis,不像Spirng Boot 2.x 預設版本的JPA 那樣DAO層方法支援返回值為Optional,所以這裡如果需要使用Optional,只能手動去使用上面列舉的第一個方法對查詢結果進行包裝 .
當然,IDEA其實已經給我們提供了該包裝方式的熱鍵了,如下圖所示 :
結語
個人的一些減少if-else
編碼習慣分享就這裡了,這幾種方式裡面,我個人覺得效果最明顯的還是第一種 提前return,提前return 也可以很好降低一段程式碼的複雜度 .
當然如果必須要使用大量的if-else
去控制邏輯時,在每個condition 標明一下注釋還是一個挺不錯的習慣 .