解鎖新姿勢(shì):探討復(fù)雜的?if-else?語句“優(yōu)雅處理”的思路
作者丨h(huán)yzhan43
來源:
juejin.im/post/5def654f51882512302daeef
前言
在之前文章說到,簡(jiǎn)單 if-else,可以使用 衛(wèi)語句 進(jìn)行優(yōu)化。但是在實(shí)際開發(fā)中,往往不是簡(jiǎn)單 if-else 結(jié)構(gòu),我們通常會(huì)不經(jīng)意間寫下如下代碼:--------------------?理想中的?if-else??--------------------
public?void?today()?{
????if?(isWeekend())?{
????????System.out.println("玩游戲");
????}?else?{
????????System.out.println("上班!");
????}
}
--------------------?現(xiàn)實(shí)中的?if-else??--------------------
if?(money?>=?1000)?{
????if?(type?==?UserType.SILVER_VIP.getCode())?{
????????System.out.println("白銀會(huì)員?優(yōu)惠50元");
????????result?=?money?-?50;
????}?else?if?(type?==?UserType.GOLD_VIP.getCode())?{
????????System.out.println("黃金會(huì)員?8折");
????????result?=?money?*?0.8;
????}?else?if?(type?==?UserType.PLATINUM_VIP.getCode())?{
????????System.out.println("白金會(huì)員?優(yōu)惠50元,再打7折");
????????result?=?(money?-?50)?*?0.7;
????}?else?{
????????System.out.println("普通會(huì)員?不打折");
????????result?=?money;
????}
}
//省略?n?個(gè)?if-else?......
毫不夸張的說,我們都寫過類似的代碼,回想起被 if-else 支配的恐懼,我們常常無所下手,甚至不了了之。下面分享一下我在開發(fā)中遇到復(fù)雜的 if-else 語句“優(yōu)雅處理”思路。如有不妥,歡迎大家一起交流學(xué)習(xí)。需求
假設(shè)有這么一個(gè)需求:一個(gè)電商系統(tǒng),當(dāng)用戶消費(fèi)滿1000 金額,可以根據(jù)用戶VIP等級(jí),享受打折優(yōu)惠。根據(jù)用戶VIP等級(jí),計(jì)算出用戶最終的費(fèi)用。
- 普通會(huì)員 不打折
- 白銀會(huì)員 優(yōu)惠50元
- 黃金會(huì)員 8折
- 白金會(huì)員 優(yōu)惠50元,再打7折
編碼實(shí)現(xiàn)
private?static?double?getResult(long?money,?int?type)?{
????double?result?=?money;
????if?(money?>=?1000)?{
????????if?(type?==?UserType.SILVER_VIP.getCode())?{
????????????System.out.println("白銀會(huì)員?優(yōu)惠50元");
????????????result?=?money?-?50;
????????}?else?if?(type?==?UserType.GOLD_VIP.getCode())?{
????????????System.out.println("黃金會(huì)員?8折");
????????????result?=?money?*?0.8;
????????}?else?if?(type?==?UserType.PLATINUM_VIP.getCode())?{
????????????System.out.println("白金會(huì)員?優(yōu)惠50元,再打7折");
????????????result?=?(money?-?50)?*?0.7;
????????}?else?{
????????????System.out.println("普通會(huì)員?不打折");
????????????result?=?money;
????????}
????}
????return?result;
}
為了方便演示,代碼上我進(jìn)行了簡(jiǎn)單實(shí)現(xiàn),但實(shí)際上 if - else 會(huì)進(jìn)行復(fù)雜的邏輯計(jì)費(fèi)。從功能上來說,基本完成,但是對(duì)于我這種有代碼潔癖的人來說,代碼質(zhì)量上不忍直視。我們開始著手 優(yōu)化一下我們的第一版代碼吧。思考
看到如上代碼,聰明的朋友首先想到的是,這不是典型的策略模式嗎?你可真是個(gè)機(jī)靈鬼,我們先嘗試用策略模式來優(yōu)化一下代碼吧。策略模式
什么是策略模式?
可能有的朋友還不清楚,什么是策略模式。策略模式是定義一系列的算法,把它們一個(gè)個(gè)封裝起來, 并且使它們可相互替換。比如上述需求,有返利、有打折、有折上折等等。這些算法本身就是一種策略。并且這些算法可以相互替換的,比如今天我想讓 白銀會(huì)員優(yōu)惠50,明天可以替換為 白銀會(huì)員打9折。說了那么多,不如編碼來得實(shí)在。編碼
public?interface?Strategy?{
????//?計(jì)費(fèi)方法
????double?compute(long?money);
}
//?普通會(huì)員策略
public?class?OrdinaryStrategy?implements?Strategy?{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("普通會(huì)員?不打折");
????????return?money;
????}
}
//?白銀會(huì)員策略
public?class?SilverStrategy?implements?Strategy?{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("白銀會(huì)員?優(yōu)惠50元");
????????return?money?-?50;
????}
}
//?黃金會(huì)員策略
public?class?GoldStrategy?implements?Strategy{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("黃金會(huì)員?8折");
????????return?money?*?0.8;
????}
}
//?白金會(huì)員策略
public?class?PlatinumStrategy?implements?Strategy?{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("白金會(huì)員?優(yōu)惠50元,再打7折");
????????return?(money?-?50)?*?0.7;
????}
}
我們定義來一個(gè) Strategy 接口,并且定義 四個(gè)子類,實(shí)現(xiàn)接口。在對(duì)應(yīng)的 compute方法 實(shí)現(xiàn)自身策略的計(jì)費(fèi)邏輯。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;
}
然后對(duì)應(yīng) getResult 方法,根據(jù) type 替換為對(duì)應(yīng)的 用戶VIP 策略。這里代碼上出現(xiàn)了重復(fù)的調(diào)用 compute ,我們可以嘗試進(jìn)一步優(yōu)化。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);
}
還記得我在第一篇中說到的衛(wèi)語句嗎?我們?cè)谶@里把 money < 1000 的情況提前 return。更關(guān)注于滿1000邏輯 ,也可以減少不必要的縮進(jìn)。深思
我曾一度 以為 策略模式不過如此。以為代碼優(yōu)化到這已經(jīng)可以了。但是還有一個(gè)恐怖的事情,if-else 依然存在 :)我嘗試翻閱了許多書籍,查看如何消除 策略模式中的 if-else書中大部分的方法是,使用簡(jiǎn)單工廠 + 策略模式。把 if - else 切換為 switch 創(chuàng)建一個(gè)工廠方法而已。但是這遠(yuǎn)遠(yuǎn)沒有達(dá)到我想要的效果,打倒 if - else直到某一天夜里,我大佬在群里分享一個(gè) Java8 小技巧時(shí),從此打開新世界。工廠 + 策略
public?interface?Strategy?{
????double?compute(long?money);
????//?返回?type
????int?getType();
}
public?class?OrdinaryStrategy?implements?Strategy?{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("普通會(huì)員?不打折");
????????return?money;
????}
????//?添加?type?返回
????@Override
????public?int?getType()?{
????????return?UserType.SILVER_VIP.getCode();
????}
}
public?class?SilverStrategy?implements?Strategy?{
????@Override
????public?double?compute(long?money)?{
????????System.out.println("白銀會(huì)員?優(yōu)惠50元");
????????return?money?-?50;
????}
????//?type?返回
????@Override
????public?int?getType()?{
????????return?UserType.SILVER_VIP.getCode();
????}
}
....省略剩下?Strategy
我們先在 Strategy 新增一個(gè) getType 方法,用來表示?該策略的 type 值。代碼相對(duì)簡(jiǎn)單,這里就不過多介紹了public?class?StrategyFactory?{
????private?Map?map;
????public?StrategyFactory()?{
????????List?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));
????????/*?等同上面
????????map?=?new?HashMap<>();
????????for?(Strategy?strategy?:?strategies)?{
????????????map.put(strategy.getType(),?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);
????}
}
靜態(tài)內(nèi)部類單例,單例模式實(shí)現(xiàn)的一種,不是本文重點(diǎn),如不了解,可以自行 google我們?cè)僦謩?chuàng)建一個(gè) StrategyFactory 工廠類。StrategyFactory 這里我使用的是靜態(tài)內(nèi)部類單例,在構(gòu)造方法的時(shí)候,初始化好 需要的 Strategy,并把 list 轉(zhuǎn)化為 map。這里 轉(zhuǎn)化就是“靈魂”所在。toMap
我們先來看看 Java8 語法中的小技巧。通常情況下,我們遍歷 List,手動(dòng)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 第一個(gè)參數(shù)是一個(gè)Function,對(duì)應(yīng) Map 中的 key,第二個(gè)參數(shù)也是一個(gè)Function,strategy -> strategy, 左邊strategy 是遍歷 strategies 中的每一個(gè)strategy,右邊strategy則是 Map 對(duì)應(yīng) value 值。若是不了解 Java8 語法的朋友,強(qiáng)烈建議看 《Java8 實(shí)戰(zhàn)》,書中詳細(xì)的介紹了 Lambda表達(dá)式、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);
}
至此,通過一個(gè)工廠類,在我們?cè)?getResult()調(diào)用的時(shí)候,根據(jù)傳入 type,即可獲取到 對(duì)應(yīng) Strategy再也沒有可怕的 if-else 語句。完結(jié)撒花撒花 : )后續(xù)
后續(xù)代碼優(yōu)化上,若是 Java 項(xiàng)目,可以嘗試使用自定義注解,注解 Strategy 實(shí)現(xiàn)類。這樣可以簡(jiǎn)化原來需在工廠類 List 添加一個(gè) Stratey 策略。最后
以上就是我在開發(fā)中遇到復(fù)雜的 if-else 語句“優(yōu)雅處理”思路,如有不妥,歡迎大家一起交流學(xué)習(xí)。
近期精彩內(nèi)容推薦:??
?Python是一門神奇的語言!
?IntelliJ IDEA 的 2020 ,真的很牛皮!
?2020年Java框架排行榜,誰居榜首?
在看點(diǎn)這里
好文分享給更多人↓↓
評(píng)論
圖片
表情
