溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么徹底干掉代碼中的if else

發(fā)布時(shí)間:2021-11-06 10:26:16 來源:億速云 閱讀:103 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“怎么徹底干掉代碼中的if else”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么徹底干掉代碼中的if else”吧!

惡心的 if-else

假設(shè)我們要做一個(gè)外賣平臺(tái),有這樣的需求:

  1. 外賣平臺(tái)上的某家店鋪為了促銷,設(shè)置了多種會(huì)員優(yōu)惠,其中包含超級(jí)會(huì)員折扣 8 折、普通會(huì)員折扣 9 折和普通用戶沒有折扣三種。

  2. 希望用戶在付款的時(shí)候,根據(jù)用戶的會(huì)員等級(jí),就可以知道用戶符合哪種折扣策略,進(jìn)而進(jìn)行打折,計(jì)算出應(yīng)付金額。

  3. 隨著業(yè)務(wù)發(fā)展,新的需求要求專屬會(huì)員要在店鋪下單金額大于 30 元的時(shí)候才可以享受優(yōu)惠。

  4. 接著,又有一個(gè)變態(tài)的需求,如果用戶的超級(jí)會(huì)員已經(jīng)到期了,并且到期時(shí)間在一周內(nèi),那么就對(duì)用戶的單筆訂單按照超級(jí)會(huì)員進(jìn)行折扣,并在收銀臺(tái)進(jìn)行強(qiáng)提醒,引導(dǎo)用戶再次開通會(huì)員,而且折扣只進(jìn)行一次。

那么,我們可以看到以下偽代碼:

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {      if (用戶是專屬會(huì)員) {         if (訂單金額大于30元) {             returen 7折價(jià)格;         }     }      if (用戶是超級(jí)會(huì)員) {         return 8折價(jià)格;     }      if (用戶是普通會(huì)員) {         if(該用戶超級(jí)會(huì)員剛過期并且尚未使用過臨時(shí)折扣){             臨時(shí)折扣使用次數(shù)更新();             returen 8折價(jià)格;         }         return 9折價(jià)格;     }     return 原價(jià); }

以上,就是對(duì)于這個(gè)需求的一段價(jià)格計(jì)算邏輯,使用偽代碼都這么復(fù)雜,如果是真的寫代碼,那復(fù)雜度可想而知。

這樣的代碼中,有很多 if-else,并且還有很多的 if-else 的嵌套,無論是可讀性還是可維護(hù)性都非常低。那么,如何改善呢?

策略模式

接下來,我們嘗試引入策略模式來提升代碼的可維護(hù)性和可讀性。

首先,定義一個(gè)接口:

/**  * @author mhcoding  */ public interface UserPayService {      /**      * 計(jì)算應(yīng)付價(jià)格      */     public BigDecimal quote(BigDecimal orderPrice); }

接著定義幾個(gè)策略類:

/**  * @author mhcoding  */ public class ParticularlyVipPayService implements UserPayService {      @Override     public BigDecimal quote(BigDecimal orderPrice) {          if (消費(fèi)金額大于30元) {             return 7折價(jià)格;         }     } }  public class SuperVipPayService implements UserPayService {      @Override     public BigDecimal quote(BigDecimal orderPrice) {         return 8折價(jià)格;     } }  public class VipPayService implements UserPayService {      @Override     public BigDecimal quote(BigDecimal orderPrice) {         if(該用戶超級(jí)會(huì)員剛過期并且尚未使用過臨時(shí)折扣){             臨時(shí)折扣使用次數(shù)更新();             returen 8折價(jià)格;         }         return 9折價(jià)格;     } }

引入了策略之后,我們可以按照如下方式進(jìn)行價(jià)格計(jì)算:

/**  * @author mhcoding  */ public class Test {      public static void main(String[] args) {         UserPayService strategy = new VipPayService();         BigDecimal quote = strategy.quote(300);         System.out.println("普通會(huì)員商品的最終價(jià)格為:" + quote.doubleValue());          strategy = new SuperVipPayService();         quote = strategy.quote(300);         System.out.println("超級(jí)會(huì)員商品的最終價(jià)格為:" + quote.doubleValue());     } }

以上,就是一個(gè)例子,可以在代碼中 New 出不同的會(huì)員的策略類,然后執(zhí)行對(duì)應(yīng)的計(jì)算價(jià)格的方法。

但是,真正在代碼中使用,比如在一個(gè) Web 項(xiàng)目中使用,上面這個(gè) Demo 根本沒辦法直接用。

首先,在 Web 項(xiàng)目中,上面我們創(chuàng)建出來的這些策略類都是被 Spring 托管的,我們不會(huì)自己去 New 一個(gè)實(shí)例出來。

其次,在 Web  項(xiàng)目中,如果真要計(jì)算價(jià)格,也是要事先知道用戶的會(huì)員等級(jí),比如從數(shù)據(jù)庫中查出會(huì)員等級(jí),然后根據(jù)等級(jí)獲取不同的策略類執(zhí)行計(jì)算價(jià)格方法。

那么,Web 項(xiàng)目中真正的計(jì)算價(jià)格的話,偽代碼應(yīng)該是這樣的:

/**  * @author mhcoding  */ public BigDecimal calPrice(BigDecimal orderPrice,User user) {       String vipType = user.getVipType();       if (vipType == 專屬會(huì)員) {         //偽代碼:從Spring中獲取超級(jí)會(huì)員的策略對(duì)象         UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);         return strategy.quote(orderPrice);      }       if (vipType == 超級(jí)會(huì)員) {         UserPayService strategy = Spring.getBean(SuperVipPayService.class);         return strategy.quote(orderPrice);      }       if (vipType == 普通會(huì)員) {         UserPayService strategy = Spring.getBean(VipPayService.class);         return strategy.quote(orderPrice);      }      return 原價(jià); }

通過以上代碼,我們發(fā)現(xiàn),代碼可維護(hù)性和可讀性好像是好了一些,但是好像并沒有減少 if-else 啊。

但是,策略模式的使用上,還是有一個(gè)比較大的缺點(diǎn)的:客戶端必須知道所有的策略類,并自行決定使用哪一個(gè)策略類。這就意味著客戶端必須理解這些算法的區(qū)別,以便適時(shí)選擇恰當(dāng)?shù)乃惴悺?/p>

也就是說,雖然在計(jì)算價(jià)格的時(shí)候沒有 if-else 了,但是選擇具體的策略的時(shí)候還是不可避免的還是要有一些 if-else。

另外,上面的偽代碼中,從 Spring 中獲取會(huì)員的策略對(duì)象我們是偽代碼實(shí)現(xiàn)的,那么代碼到底該如何獲取對(duì)應(yīng)的 Bean 呢?

接下來我們看如何借助 Spring 和工廠模式,解決上面這些問題。

工廠模式

為了方便我們從 Spring 中獲取 UserPayService 的各個(gè)策略類,我們創(chuàng)建一個(gè)工廠類:

/**  * @author mhcoding  */ public class UserPayServiceStrategyFactory {      private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();      public  static UserPayService getByUserType(String type){         return services.get(type);     }      public static void register(String userType,UserPayService userPayService){         Assert.notNull(userType,"userType can't be null");         services.put(userType,userPayService);     } }

這個(gè) UserPayServiceStrategyFactory 中定義了一個(gè) Map,用來保存所有的策略類的實(shí)例,并提供一個(gè) getByUserType  方法,可以根據(jù)類型直接獲取對(duì)應(yīng)的類的實(shí)例。還有一個(gè) Register 方法,這個(gè)后面再講。

有了這個(gè)工廠類之后,計(jì)算價(jià)格的代碼即可得到大大的優(yōu)化:

/**  * @author mhcoding  */ public BigDecimal calPrice(BigDecimal orderPrice,User user) {       String vipType = user.getVipType();      UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);      return strategy.quote(orderPrice); }

以上代碼中,不再需要 if-else 了,拿到用戶的 vip 類型之后,直接通過工廠的 getByUserType 方法直接調(diào)用就可以了。

通過策略+工廠,我們的代碼很大程度的優(yōu)化了,大大提升了可讀性和可維護(hù)性。

但是,上面還遺留了一個(gè)問題,那就是 UserPayServiceStrategyFactory 中用來保存所有的策略類的實(shí)例的 Map  是如何被初始化的?各個(gè)策略的實(shí)例對(duì)象如何塞進(jìn)去的呢?

Spring Bean 的注冊(cè)

還記得我們前面定義的 UserPayServiceStrategyFactory 中提供了的 Register 方法嗎?他就是用來注冊(cè)策略服務(wù)的。

接下來,我們就想辦法調(diào)用 Register 方法,把 Spring 通過 IOC 創(chuàng)建出來的 Bean 注冊(cè)進(jìn)去就行了。

這種需求,可以借用 Spring 中提供的 InitializingBean 接口,這個(gè)接口為 Bean 提供了屬性初始化后的處理方法。

它只包括 afterPropertiesSet 方法,凡是繼承該接口的類,在 Bean 的屬性初始化后都會(huì)執(zhí)行該方法。

那么,我們將前面的各個(gè)策略類稍作改造即可:

/**  * @author mhcoding  */ @Service public class ParticularlyVipPayService implements UserPayService,InitializingBean {      @Override     public BigDecimal quote(BigDecimal orderPrice) {          if (消費(fèi)金額大于30元) {             return 7折價(jià)格;         }     }      @Override     public void afterPropertiesSet() throws Exception {         UserPayServiceStrategyFactory.register("ParticularlyVip",this);     } }  @Service public class SuperVipPayService implements UserPayService ,InitializingBean{      @Override     public BigDecimal quote(BigDecimal orderPrice) {         return 8折價(jià)格;     }      @Override     public void afterPropertiesSet() throws Exception {         UserPayServiceStrategyFactory.register("SuperVip",this);     } }  @Service   public class VipPayService implements UserPayService,InitializingBean {      @Override     public BigDecimal quote(BigDecimal orderPrice) {         if(該用戶超級(jí)會(huì)員剛過期并且尚未使用過臨時(shí)折扣){             臨時(shí)折扣使用次數(shù)更新();             returen 8折價(jià)格;         }         return 9折價(jià)格;     }      @Override     public void afterPropertiesSet() throws Exception {         UserPayServiceStrategyFactory.register("Vip",this);     } }

只需要每一個(gè)策略服務(wù)的實(shí)現(xiàn)類都實(shí)現(xiàn) InitializingBean 接口,并實(shí)現(xiàn)其 afterPropertiesSet 方法,在這個(gè)方法中調(diào)用  UserPayServiceStrategyFactory.register 即可。

這樣,在 Spring 初始化的時(shí)候,當(dāng)創(chuàng)建 VipPayService、SuperVipPayService 和  ParticularlyVipPayService 的時(shí)候,會(huì)在 Bean 的屬性初始化之后,把這個(gè) Bean 注冊(cè)到  UserPayServiceStrategyFactory 中。

以上代碼,其實(shí)還是有一些重復(fù)代碼的,這里面還可以引入模板方法模式進(jìn)一步精簡,這里就不展開了。

還有就是,UserPayServiceStrategyFactory.register  調(diào)用的時(shí)候,第一個(gè)參數(shù)需要傳一個(gè)字符串,這里的話其實(shí)也可以優(yōu)化掉。

比如使用枚舉,或者在每個(gè)策略類中自定義一個(gè) getUserType 方法,各自實(shí)現(xiàn)即可。

到此,相信大家對(duì)“怎么徹底干掉代碼中的if else”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI