回想起被 `if-else` 支配的恐懼,下定決心我要打倒 if



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

回想起被 `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;
}
}
//省略 n 個 if-else ......

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

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


01 需求

假設有這麼一個需求:

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

根據用戶VIP等級,計算出用戶最終的費用。

  • 普通會員 不打折
  • 白銀會員 優惠50元
  • 黃金會員 8折
  • 白金會員 優惠50元,再打7折

02 編碼實現

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 會進行復雜的邏輯計費。 從功能上來說,基本完成,但是對於我這種有代碼潔癖的人來說,代碼質量上不忍直視。我們開始著手 優化一下我們的第一版代碼吧。

03 思考

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

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

04 策略模式

(1)什麼是策略模式?

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

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

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

(2)編碼

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邏輯 ,也可以減少不必要的縮進。

05 深思

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

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

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

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

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

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

06 工廠 + 策略

public interface Strategy {
double compute(long money);

// 返回 type

int getType();

}

public class OrdinaryStrategy implements Strategy {

@Override

public double compute(long money) {

System.out.println("普通會員 不打折");

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("白銀會員 優惠50元");

return money - 50;

}

// type 返回

@Override

public int getType() {

return UserType.SILVER_VIP.getCode();

}

}

....省略剩下 Strategy

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

public class StrategyFactory {
private Map<integer> 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));



/* 等同上面

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);

}

}/<strategy>/<integer>

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

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

(1)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));

(2)效果

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 語句。

07 後續

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

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

最後

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


作者:hyzhan43
原文鏈接:https://juejin.im/post/5def654f51882512302daeef


分享到:


相關文章: