大家好,我是R哥。
最近做 Java 面試輔導,有個兄弟面試美團,遇到一個特別有意思的問題:
一萬個 if else 如何最佳化,有好的解決方案嗎?
我看到這問題都有點懵逼,現實專案中怎麼可能會有 10000 個 if else 的程式碼,至少我工作 10 餘年沒見過樣的程式碼。
關鍵要寫完這 10000 行的 if else 程式碼,如果每天寫 100 個,是不是意味著也要 100 天才能寫完,並且每次請求都要執行幾千上萬次的判斷,程式碼難以維護不說,還會嚴重影響系統效能。
很顯然,面試官考察的不是一般的八股文,這個問題可以看作是一道場景題,它考察一個程式設計師在面對複雜邏輯判斷時的最佳化能力,也是在考察一個程式設計師臨場發揮技術能力。
這兄弟雖然說上了策略模式,但顯然不是完美和唯一的解決方案,另外像責任鏈模式等其他設計模式都會存在這樣的問題,所以具體的問題還得具體分析。
所以這題可以這樣回答:
- 如果這 1 萬個 if else 是在同一個程式碼塊流程裡面,這就要考慮這 1 萬 if else 存在的意義了,因為這麼量極的 if else 會很難維護,也會極其影響效能,需要具體分析然後再去分析如何去分解和最佳化。
- 如果這 1 萬個 if else 分散在同一個專案裡面,那麼最佳化 if else 的方式有很多種,包括.……
下面我說說幾種方案,歡迎大家拍磚。
if else 最佳化方案
方案1:策略模式
這個兄弟也說到了策略模式,策略模式介紹及實戰看這篇:
別在再滿屏的 if/ else 了,試試策略模式,真香!!
使用策略模式確實可以提升程式碼的優雅性,但也會存在以下問題:
- 如果是大量的 if else 分支,比如這 1 萬個,那就會有 1 萬個策略類,此時就會造成類膨脹,並且隨著時間的推移逐漸變得更加龐大而複雜。
- 如果是多層的 if else 巢狀,策略模式可能也無法派上用場了。
策略模式的優點是可以很方便的解耦,適用於有多種不同邏輯和演算法的 if 場景,但不適用於大量的 if else 場景。
方案2:策略模式變體
這是策略模式的一種變體:
Map<Integer, Runnable> actionMap = new HashMap<>();
actionMap.put("condition1", () -> { /* 分支1的執行邏輯 */ });
actionMap.put("condition2", () -> { /* 分支2的執行邏輯 */ });
actionMap.put("conditionN", () -> { /* 分支N的執行邏輯 */ });
// 根據條件獲取執行邏輯
Runnable action = actionMap.get("condition1");
if (action != null) {
action.run();
}
這種把業務邏輯程式碼分離出去了,簡化了單個類的程式碼,也省去了策略實現類,讓策略類不會得到膨脹,但如果有大量的條件對映,依然會造成單個類的膨脹和難以維護。
這裡使用的是執行緒非同步執行的案例,還可以把要執行的邏輯程式碼儲存在其他類、資料庫中,然後再用反射或者動態編譯的方式載入進去並執行。
方案3:多級巢狀最佳化
上面說的兩種方案巢狀可能無法解決,如果是這種帶層級的判斷是可以最佳化的:
/*
* 來源公眾號:Java技術棧
*/
if(xxxOrder != null){
if(xxxOrder.getXxxShippingInfo() != null){
if(xxxOrder.getXxxShippingInfo().getXxxShipmentDetails() != null){
if(xxxOrder.getXxxShippingInfo().getXxxShipmentDetails().getXxxTrackingInfo() != null){
...
}
}
}
}
這種 if 巢狀層級太多,極不優雅,怎麼最佳化見我之前寫的這篇:
if else 太多?看我用 Java 8 輕鬆幹掉!
方案4:使用三目運算子
如果判斷條件不多,只有 2、3 個的情況下可以使用三目運算子簡化 if else 分支。
比如以下程式碼:
String desc;
if (condition1) {
desc = "XX1";
} else if (condition2) {
desc = "XX2";
} else {
desc = "XX3";
}
使用三目運算子一行搞定:
String desc = condition1 ? "XX1" : (condition2 ? "XX2" : "XX3");
超過 3 個條件就不建議使用了,不然程式碼可讀性會大大降低。
方案5:使用列舉
列舉型別可以用來表示一組固定的值,例如星期幾、月份、顏色等,它提供了一種更簡潔、可讀性更高的方式來表示一組相關的常量。
如以下示例程式碼:
/**
* 公眾號:Java技術棧
*/
public class Test {
public static void main(String[] args) {
Day today = Day.MONDAY;
System.out.println("Today is " + today);
System.out.println("Today is " + today.getChineseName());
}
enum Day {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private String chineseName;
Day(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() {
return chineseName;
}
}
}
這裡我只寫了一個欄位,我們可以在列舉屬性裡面定義多個欄位,這樣就無需大量的 if else 判斷,直接透過列舉來獲取某個某一組固定的值了。
方案6:使用 Optional
Java 8 提供了一個 Optional 新特性,它是一個可以包含 null 值的容器物件,可以用來代替 xx != null 的判斷。
參考我之前寫的這篇:
JDK 8 新特性之 Optional
如果專案中存在大量 xx != null 的判斷,可以使用 Optional 來最佳化。
方案7:儘快返回
分析業務,根據 if else 的執行次數按降序排,把執行次數較多的 if 放在最前面,如果符合條件,就使用 return 返回,如下面程式碼:
if (條件1) {
return
}
if (條件2) {
return
}
...
這樣改可能是比較簡單的方式,在很大程度上可以提升系統的效能,但是還存在以下問題:
- 有的條件不能按執行次數排序,存在先後或者互斥關係。
- 如果新增一個條件,可能無法馬上判定它的執行次數,如果放在後面可能也還會影響效能。
- 對類的繼續膨脹和程式碼維護沒有任何幫助。
方案8:去除沒必要的 if else
比如這種:
if (condition) {
...
} else {
return;
}
最佳化後:
if(!condition){
return;
}
或者是這樣:
return !condition
方案9:合併條件
考慮這 1 萬 if else 是不是真的每個都有必要,是不是可以合併歸類,比如是不是可以把幾百、幾千個相似邏輯的歸為一類,這樣也能大大簡化 if else 數量。
比如以下程式碼:
double calculateShipping() {
if (orderAmount > 1000) {
return 0.5;
}
if (customerLoyaltyLevel > 5) {
return 0.5;
}
if (promotionIsActive) {
return 0.5;
}
}
最佳化後:
double calculateShipping() {
if (orderAmount > 1000 || customerLoyaltyLevel > 5 || promotionIsActive) {
return 0.5;
}
}
這樣就把返回相同值的 if 歸為一類了,如果 if 較大就能大大簡化程式碼量。
方案10:規則引擎
對於複雜的業務邏輯,業務規則經常變化,規則制定不依賴於技術團隊,需要實現可配置的邏輯處理,此時可以考慮使用規則引擎來處理,比如 Drools。
規則引擎系統可用於執行一組規則,在許多業務應用程式中,業務決策可以透過一系列的邏輯規則來定義,規則引擎允許這些規則在執行時執行,而無需硬編碼在應用程式之中。
規則引擎的好處是:
- 業務邏輯可以和程式程式碼解耦;
- 提高業務邏輯的可管理性;
- 提高系統的靈活性和可擴充套件性;
- 業務人員可參與決策過程;
總結
掌握最佳化 if else 的方法很重要,有時候面試官可能會換著法子問你,比如我們面試輔導這位兄弟遇到的這個面試官,你可以問清楚這一萬個 if else 是在一個程式碼塊中,還是在一個專案中,然後再去解答,如果不瞭解清楚業務場景,盲目回答又會被面試官繞進去。
本文我也總結了 10 種最佳化 if else 的方法,其實還不止,根據不同的場景還可以使用多型、責任鏈模式、模板方法模式等更多方法來消除 if else。
總之,消除 if else 並沒有萬能的方法,也不可能全部最佳化掉,在實際開發中需要根據實際場景使用不同的方法,以及多種方法組合使用,這樣才是正確的方式。
像這樣的面試題,我的小程式「Java面試庫」還有許多,比如:
- 如果把執行緒池 corePoolSize 設定為 0,會出現什麼情況?
- 執行緒池中的執行緒丟擲了異常,如何處理?
- MySQL 索引為什麼選用 B+Tree 作為資料結構?
- MySQL 的自增 ID 如果用完了,會怎麼樣?
- 有了多執行緒,為什麼還要訊息佇列?
- ......
共 2800+,都是平時我面試別人,或者學員面試覆盤積累下來的真題,不要在網上找亂七八糟的面試題了,浪費時間還容易被誤導。
最後,推薦一波我們的「面試輔導」,有在看機會的,離職的、迷茫的,都可以加入,大廠導師 1 v 1 輔導,幫你全面提升面試綜合實力,少走很多彎路,最大化提升職場收益。
版權宣告: 本文系公眾號 "Java技術棧" 原創,轉載、引用本文內容請註明出處,抄襲、洗稿一律投訴侵權,後果自負,並保留追究其法律責任的權利。
更多文章推薦:
1.Spring Boot 3.x 教程,太全了!
2.2,000+ 道 Java面試題及答案整理(2024最新版)
3.免費獲取 IDEA 啟用碼的 7 種方式(2024最新版)
覺得不錯,別忘了隨手點贊+轉發哦!