美團一面:專案中有 10000 個 if else 如何最佳化?想了半天,被問懵了!

Java技术栈發表於2024-05-21

大家好,我是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最新版)

覺得不錯,別忘了隨手點贊+轉發哦!

相關文章