解鎖新姿勢:探討複雜的 if-else 語句「優雅處理」的思路

2021-02-21 Java知音

點擊上方「Java知音」,選擇「置頂公眾號」

技術文章第一時間送達!

作者:hyzhan43

juejin.im/post/5def654f51882512302daeef

前言

在之前文章說到,簡單 if-else,可以使用 衛語句 進行優化。但是在實際開發中,往往不是簡單 if-else 結構,我們通常會不經意間寫下如下代碼:

 理想中的 if-else  
public void today() {
    if (isWeekend()) {
        System.out.println("玩遊戲");
    } else {
        System.out.println("上班!");
    }
}


 現實中的 if-else  

if (money >= 1000) {
    if (type == UserType.SILVER_VIP.getCode()) {

        System.out.println("白銀會員 優惠50元");
        result = money - 50;
    } else if (type == UserType.GOLD_VIP.getCode()) {

        System.out.println("黃金會員 8折");
        result = money * 0.8;
    } else if (type == UserType.PLATINUM_VIP.getCode()) {

        System.out.println("白金會員 優惠50元,再打7折");
        result = (money - 50) * 0.7;
    } else {
        System.out.println("普通會員 不打折");
        result = money;
    }
}



毫不誇張的說,我們都寫過類似的代碼,回想起被 if-else 支配的恐懼,我們常常無所下手,甚至不了了之。

下面分享一下我在開發中遇到複雜的 if-else 語句「優雅處理」思路。如有不妥,歡迎大家一起交流學習。

需求

假設有這麼一個需求:

一個電商系統,當用戶消費滿1000 金額,可以根據用戶VIP等級,享受打折優惠。
根據用戶VIP等級,計算出用戶最終的費用。

普通會員 不打折

白銀會員 優惠50元

黃金會員 8折

白金會員 優惠50元,再打7折

編碼實現

private static double getResult(long money, int type) {

    double result = money;

    if (money >= 1000) {
        if (type == UserType.SILVER_VIP.getCode()) {

            System.out.println("白銀會員 優惠50元");
            result = money - 50;
        } else if (type == UserType.GOLD_VIP.getCode()) {

            System.out.println("黃金會員 8折");
            result = money * 0.8;
        } else if (type == UserType.PLATINUM_VIP.getCode()) {

            System.out.println("白金會員 優惠50元,再打7折");
            result = (money - 50) * 0.7;
        } else {
            System.out.println("普通會員 不打折");
            result = money;
        }
    }

    return result;
}

為了方便演示,代碼上我進行了簡單實現,但實際上 if - else 會進行複雜的邏輯計費。從功能上來說,基本完成,但是對於我這種有代碼潔癖的人來說,代碼質量上不忍直視。我們開始著手 優化一下我們的第一版代碼吧。

思考

看到如上代碼,聰明的朋友首先想到的是,這不是典型的策略模式嗎?

你可真是個機靈鬼,我們先嘗試用策略模式來優化一下代碼吧。

策略模式什麼是策略模式?

可能有的朋友還不清楚,什麼是策略模式。策略模式是定義一系列的算法,把它們一個個封裝起來, 並且使它們可相互替換。

比如上述需求,有返利、有打折、有折上折等等。這些算法本身就是一種策略。並且這些算法可以相互替換的,比如今天我想讓 白銀會員優惠50,明天可以替換為 白銀會員打9折。

說了那麼多,不如編碼來得實在。

編碼

public interface Strategy {

    
    double compute(long money);
}


public class OrdinaryStrategy implements Strategy {

    @Override
    public double compute(long money) {
        System.out.println("普通會員 不打折");
        return money;
    }
}


public class SilverStrategy implements Strategy {

    @Override
    public double compute(long money) {

        System.out.println("白銀會員 優惠50元");
        return money - 50;
    }
}


public class GoldStrategy implements Strategy{

    @Override
    public double compute(long money) {
        System.out.println("黃金會員 8折");
        return money * 0.8;
    }
}


public class PlatinumStrategy implements Strategy {
    @Override
    public double compute(long money) {
        System.out.println("白金會員 優惠50元,再打7折");
        return (money - 50) * 0.7;
    }
}

我們定義來一個 Strategy 接口,並且定義 四個子類,實現接口。在對應的 compute方法 實現自身策略的計費邏輯。

private static double getResult(long money, int type) {

    double result = money;

    if (money >= 1000) {
        if (type == UserType.SILVER_VIP.getCode()) {

            result = new SilverStrategy().compute(money);
        } else if (type == UserType.GOLD_VIP.getCode()) {

            result = new GoldStrategy().compute(money);
        } else if (type == UserType.PLATINUM_VIP.getCode()) {

            result = new PlatinumStrategy().compute(money);
        } else {
            result = new OrdinaryStrategy().compute(money);
        }
    }

    return result;
}

然後對應 getResult 方法,根據 type 替換為對應的 用戶VIP 策略。這裡代碼上出現了重複的調用 compute ,我們可以嘗試進一步優化。

private static double getResult(long money, int type) {

    if (money < 1000) {
        return money;
    }

    Strategy strategy;

    if (type == UserType.SILVER_VIP.getCode()) {
        strategy = new SilverStrategy();
    } else if (type == UserType.GOLD_VIP.getCode()) {
        strategy = new GoldStrategy();
    } else if (type == UserType.PLATINUM_VIP.getCode()) {
        strategy = new PlatinumStrategy();
    } else {
        strategy = new OrdinaryStrategy();
    }

    return strategy.compute(money);
}

還記得我在第一篇中說到的衛語句嗎?我們在這裡把 money < 1000 的情況提前 return。更關注於滿1000邏輯 ,也可以減少不必要的縮進。

深思

我曾一度 以為 策略模式不過如此。以為代碼優化到這已經可以了。

但是還有一個恐怖的事情,if-else 依然存在 :)

我嘗試翻閱了許多書籍,查看如何消除 策略模式中的 if-else

書中大部分的方法是,使用簡單工廠 + 策略模式。把 if - else 切換為 switch 創建一個工廠方法而已。

但是這遠遠沒有達到我想要的效果,打倒 if - else

直到某一天夜裡,我大佬在群裡分享一個 Java8 小技巧時,從此打開新世界。

工廠 + 策略

public interface Strategy {

    double compute(long money);

    
    int getType();
}


public class OrdinaryStrategy implements Strategy {

    @Override
    public double compute(long money) {
        System.out.println("普通會員 不打折");
        return money;
    }

    
    @Override
    public int getType() {
        return UserType.SILVER_VIP.getCode();
    }
}

public class SilverStrategy implements Strategy {

    @Override
    public double compute(long money) {

        System.out.println("白銀會員 優惠50元");
        return money - 50;
    }

    
    @Override
    public int getType() {
        return UserType.SILVER_VIP.getCode();
    }
}

....省略剩下 Strategy

我們先在 Strategy 新增一個 getType 方法,用來表示 該策略的 type 值。代碼相對簡單,這裡就不過多介紹了

public class StrategyFactory {

    private Map<Integer, Strategy> map;

    public StrategyFactory() {

        List<Strategy> strategies = new ArrayList<>();

        strategies.add(new OrdinaryStrategy());
        strategies.add(new SilverStrategy());
        strategies.add(new GoldStrategy());
        strategies.add(new PlatinumStrategy());
        strategies.add(new PlatinumStrategy());

        
        map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));

        
    }

    public static class Holder {
        public static StrategyFactory instance = new StrategyFactory();
    }

    public static StrategyFactory getInstance() {
        return Holder.instance;
    }

    public Strategy get(Integer type) {
        return map.get(type);
    }
}

靜態內部類單例,單例模式實現的一種,不是本文重點,如不了解,可以自行 google

我們再著手創建一個 StrategyFactory 工廠類。StrategyFactory 這裡我使用的是靜態內部類單例,在構造方法的時候,初始化好 需要的 Strategy,並把 list 轉化為 map。

這裡 轉化就是「靈魂」所在。

toMap

我們先來看看 Java8 語法中的小技巧。
通常情況下,我們遍歷 List,手動put到 Map 中。

----  before --

map = new HashMap<>();
for (Strategy strategy : strategies) {
    map.put(strategy.getType(), strategy);
}

----  after Java8 --

map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));

toMap 第一個參數是一個Function,對應 Map 中的 key,第二個參數也是一個Function,strategy -> strategy, 左邊strategy 是遍歷 strategies 中的每一個strategy,右邊strategy則是 Map 對應 value 值。

若是不了解 Java8 語法的朋友,強烈建議看 《Java8 實戰》,書中詳細的介紹了 Lambda表達式、Stream等語法。

效果

private static double getResult(long money, int type) {

    if (money < 1000) {
        return money;
    }

    Strategy strategy = StrategyFactory.getInstance().get(type);

    if (strategy == null){
        throw new IllegalArgumentException("please input right type");
    }

    return strategy.compute(money);
}

至此,通過一個工廠類,在我們在 getResult()調用的時候,根據傳入 type,即可獲取到 對應 Strategy

再也沒有可怕的 if-else 語句。

完結撒花撒花 : )

後續

後續代碼優化上,若是 Java 項目,可以嘗試使用自定義註解,註解 Strategy 實現類。

這樣可以簡化原來需在工廠類 List 添加一個 Stratey 策略。

最後

以上就是我在開發中遇到複雜的 if-else 語句「優雅處理」思路,如有不妥,歡迎大家一起交流學習。

END

Java面試題專欄

我知道你 「在看

相關焦點

  • Excel VBA解讀(165):錯誤處理技術之On Error語句的使用
    當使用On Error語句時,VBA會修改錯誤處理操作,並忽略前面默認或指定的錯誤處理操作。 下面的過程演示了使用On Error語句改變VBA的錯誤處理操作:Sub ErrorStates()    Dim x As Long       '如果發生錯誤則跳至標籤errH    On Error GoTo errH       '忽略接下來語句行發生的錯誤
  • Python 炫技操作:條件語句的七種寫法
    本文我將寫一寫很簡單的條件判斷語句裡那些炫技操作,在這裡,如果你是 Python 發燒友,你可以學到一些寫出超酷的代碼書寫技巧,但學習歸學習,希望你區分場景使用。這是一段非常簡單的通過年齡判斷一個人是否成年的代碼,由於代碼行數過多,有些人就不太願意這樣寫,因為這體現不出自己多年的 Python 功力。
  • 比王思聰能"作"100倍, 澳洲菸草大王私生活:女人多到缺覺、每周解鎖一個新姿勢……老婆還一點都不介意!
    【今日澳洲4月3日訊】澳菸草巨頭Travers 'Candyman' Beynon私生活大爆料:平均每晚和4名女子睡、每周「解鎖」一個新姿勢……他老婆還毫不介意!Beynon稱每晚和一堆女人「共度春宵」還是有點難度的,「每天晚上我的床上至少有四個女人,我總是缺覺……我覺得我現在每周都能『解鎖』一個新姿勢。」
  • This Website That Turns Them Into Anime Characters
  • 蘋果調查:iPhone 13是否加入指紋解鎖?
    早在今年新iPhone發布之前,蘋果就曾針對 iPhone 包裝盒中的 USB 電源適配器的使用情況進行過調查。
  • 唯美的歌曲: 《I Shall Not Want 願憐憫你》
    《I Shall Not Want 願憐憫你》她的音樂風格混合了宗教與非宗教的主題,更多的是為上帝唱歌而不是唱關於上帝的歌!她出生在宗教信仰的家庭裡,成長過程中受母親影響,聽過不少世俗音樂。也受到父親的影響吸收了法國與中東的音樂。
  • 警惕新詐騙:盜蘋果帳號 想解鎖請給錢!
    新詐騙:盜蘋果帳號 遠程鎖設備廣州的何小姐使用的是蘋果手機,從上個月底開始,她發現蘋果帳戶的密碼無論怎麼輸入,都顯示錯誤無奈之下何小姐唯有聯繫盜號者預留的QQ號碼,對方就表示自己只是幫忙解鎖設備,並非盜號本人。但想要解鎖就要給錢,盛惠400大元。「不給錢不解鎖」,對方態度極為囂張!
  • 急生產所急,展現我們的風採   ———CEMS故障搶修紀實
    急生產所急,展現我們的風採               ———CEMS故障搶修紀實2021.1.23晚17時42分,制氫裝置CEMS系統檢測值突然到零。CEMS連續環境檢測系統檢測的是整個制氫裝置氮氧化物排放含量,如不及時處理,錯誤的數據會傳輸到國家的環保平臺,極易造成環保事件導致公司不合法生產。儀表人員在接到工程師指令後,顧不上吃晚飯,迅速派出搶修人員趕赴現場。在與操作人員共同核實後,首先排除了工藝波動原因。經過檢查發現分析儀伴熱溫度降至80攝氏度、分析儀進樣停止、冷凝器失電停運、分析儀自動切換到零點標定,導致遠傳數據到零。
  • 越獄插件:讓iPhone解鎖畫面數字鍵盤變的很不一樣!
    相信不少iPhone用戶對於鍵盤的風格已經看膩了,之前可能有別的辦法通過手動方式修改撥號按鈕背景圖,但只是單純一張照片,而且步驟相當複雜,其實修改一下撥號鍵盤那需要這麼複雜
  • 破解Face ID新招:使用眼鏡和膠帶可解鎖iPhone
    高性價比二手機:iPhoneX,三網通9.8新,微信購買:64G,4500元,256G 4999元,和新機差價大,新機漲價我們沒漲價,OLED暗黑模式超級省電你值得擁有
  • 題源外刊(中英對照) | 迷奸、偷拍和毒品:韓國娛樂圈地震——勝利醜聞事件
    (本文選自《經濟學人》20190330期)背景介紹:      上個月,韓國流行樂團 Bigbang 前成員勝利被曝出醜聞,這一事件也引發了韓國娛樂圈的地震。Jung Joon-young, a singer, songwriter and former television host, has been arrested on charges of filming and distributing illegal spy-cam footage.
  • 一場中秋「團圓飯」  烹出文明新風尚
    鄰裡同攜手,共創文明新村。一場 「團圓飯」彰顯文明「滿堂紅」。走進祠堂,牆上張貼的是「陳家祖訓」「陳氏家規家訓」,以及「蘆衣順母」「鹿乳奉親」「棄官尋母」等24個經典孝親歷史故事。鄰裡同攜手,共創文明新村。活動過程中,村民們積極響應耒陽市文明城市創建精神,光碟行動、餐廚垃圾分類.以健康、衛生、文明的用餐方式,踐行「我是文明耒陽人」的精神風貌。
  • 是設計的  是參與的  也是被引領的 一一臺灣高速公路生活服務區考察之一
    今年9月12日,由重慶市人力資源和社會保障局、共青團重慶市委員會、重慶高速公路集團有限公司共同主辦的「渝創渝新,夢想高速」重慶高速公路服務區青年創業創新大賽啟動
  • MacID 更新:它可以用 Touch ID 解鎖Mac
    MacID 是一款橫跨 iOS 和 OS X
  • 三大新技能解鎖:ThinkStation P310工作站新年登場
    新品在傳承Think三大品質的同時,還為專業用戶解鎖了三大新技能!想知道,咱往下看~作為聯想ThinkStation P系列工作站引以為傲的獨家多通道散熱技術,利用通道空氣擋板的獨特設計,最大限度提高進氣量,將冷空氣直接導向關鍵系統部件進行冷卻,進而確保關鍵部件長久處於最佳運行狀態。
  • 撿到一個蘋果手機,該如何解鎖?
    網友諮詢撿到一個iPhone如何解鎖!  按照目前iOS的安全性來說,想解鎖一個撿到的蘋果,簡直比登天還難!
  • 翻譯思路拆解 | 2020年CATTI三級筆譯中譯英:access比你想像的要靈活
    以下譯文及思路拆解由譯群人翻譯團隊原創,拋磚引玉,如有瑕疵,歡迎友好交流探討哈~ 第一句中國歡迎公平、開放、競爭的市場,在自身發展的同時,致力於推動全球數字經濟發展。 「歡迎」,可以是動詞encourage, embrace,也可用名詞性be an advocate of...。如果用名詞性的話,後面的「致力於」也可以處理為名詞a contributor to...
  • 過完新年的日本人又解鎖了各種奇葩姿勢…
    這個睡覺姿勢暴露出了身材缺陷。這個大叔的姿勢就比樓上的小哥酷多了。尤其是坐過終電的小夥伴,相信能見識到更多奇葩姿勢……友善提醒:一定要離終電上的醉漢們遠一點,因為他們隨時都會吐!畢竟真的遇到過鄰座突然瘋狂嘔吐的事情,那味道、那場景……真的留下了不小的心理陰影……
  • The Nature and Scope of Economics
    The definitions are: 1. General Definition of Economics 2. Adam Smith’s Wealth Definition 3. Marshall’s Welfare Definition 4. Robbins』 Scarcity Definition.
  • 英語歌曲推薦:Anyone Else But You
    Anyone Else But You- The  Moldy PeachesYou're a part time lover and a full time friend你有時是個情人,而永遠都是朋友The monkey on you're back is the latest trend