您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么徹底干掉代碼中的if else”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么徹底干掉代碼中的if else”吧!
惡心的 if-else
假設我們要做一個外賣平臺,有這樣的需求:
外賣平臺上的某家店鋪為了促銷,設置了多種會員優惠,其中包含超級會員折扣 8 折、普通會員折扣 9 折和普通用戶沒有折扣三種。
希望用戶在付款的時候,根據用戶的會員等級,就可以知道用戶符合哪種折扣策略,進而進行打折,計算出應付金額。
隨著業務發展,新的需求要求專屬會員要在店鋪下單金額大于 30 元的時候才可以享受優惠。
接著,又有一個變態的需求,如果用戶的超級會員已經到期了,并且到期時間在一周內,那么就對用戶的單筆訂單按照超級會員進行折扣,并在收銀臺進行強提醒,引導用戶再次開通會員,而且折扣只進行一次。
那么,我們可以看到以下偽代碼:
public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) { if (用戶是專屬會員) { if (訂單金額大于30元) { returen 7折價格; } } if (用戶是超級會員) { return 8折價格; } if (用戶是普通會員) { if(該用戶超級會員剛過期并且尚未使用過臨時折扣){ 臨時折扣使用次數更新(); returen 8折價格; } return 9折價格; } return 原價; }
以上,就是對于這個需求的一段價格計算邏輯,使用偽代碼都這么復雜,如果是真的寫代碼,那復雜度可想而知。
這樣的代碼中,有很多 if-else,并且還有很多的 if-else 的嵌套,無論是可讀性還是可維護性都非常低。那么,如何改善呢?
策略模式
接下來,我們嘗試引入策略模式來提升代碼的可維護性和可讀性。
首先,定義一個接口:
/** * @author mhcoding */ public interface UserPayService { /** * 計算應付價格 */ public BigDecimal quote(BigDecimal orderPrice); }
接著定義幾個策略類:
/** * @author mhcoding */ public class ParticularlyVipPayService implements UserPayService { @Override public BigDecimal quote(BigDecimal orderPrice) { if (消費金額大于30元) { return 7折價格; } } } public class SuperVipPayService implements UserPayService { @Override public BigDecimal quote(BigDecimal orderPrice) { return 8折價格; } } public class VipPayService implements UserPayService { @Override public BigDecimal quote(BigDecimal orderPrice) { if(該用戶超級會員剛過期并且尚未使用過臨時折扣){ 臨時折扣使用次數更新(); returen 8折價格; } return 9折價格; } }
引入了策略之后,我們可以按照如下方式進行價格計算:
/** * @author mhcoding */ public class Test { public static void main(String[] args) { UserPayService strategy = new VipPayService(); BigDecimal quote = strategy.quote(300); System.out.println("普通會員商品的最終價格為:" + quote.doubleValue()); strategy = new SuperVipPayService(); quote = strategy.quote(300); System.out.println("超級會員商品的最終價格為:" + quote.doubleValue()); } }
以上,就是一個例子,可以在代碼中 New 出不同的會員的策略類,然后執行對應的計算價格的方法。
但是,真正在代碼中使用,比如在一個 Web 項目中使用,上面這個 Demo 根本沒辦法直接用。
首先,在 Web 項目中,上面我們創建出來的這些策略類都是被 Spring 托管的,我們不會自己去 New 一個實例出來。
其次,在 Web 項目中,如果真要計算價格,也是要事先知道用戶的會員等級,比如從數據庫中查出會員等級,然后根據等級獲取不同的策略類執行計算價格方法。
那么,Web 項目中真正的計算價格的話,偽代碼應該是這樣的:
/** * @author mhcoding */ public BigDecimal calPrice(BigDecimal orderPrice,User user) { String vipType = user.getVipType(); if (vipType == 專屬會員) { //偽代碼:從Spring中獲取超級會員的策略對象 UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class); return strategy.quote(orderPrice); } if (vipType == 超級會員) { UserPayService strategy = Spring.getBean(SuperVipPayService.class); return strategy.quote(orderPrice); } if (vipType == 普通會員) { UserPayService strategy = Spring.getBean(VipPayService.class); return strategy.quote(orderPrice); } return 原價; }
通過以上代碼,我們發現,代碼可維護性和可讀性好像是好了一些,但是好像并沒有減少 if-else 啊。
但是,策略模式的使用上,還是有一個比較大的缺點的:客戶端必須知道所有的策略類,并自行決定使用哪一個策略類。這就意味著客戶端必須理解這些算法的區別,以便適時選擇恰當的算法類。
也就是說,雖然在計算價格的時候沒有 if-else 了,但是選擇具體的策略的時候還是不可避免的還是要有一些 if-else。
另外,上面的偽代碼中,從 Spring 中獲取會員的策略對象我們是偽代碼實現的,那么代碼到底該如何獲取對應的 Bean 呢?
接下來我們看如何借助 Spring 和工廠模式,解決上面這些問題。
工廠模式
為了方便我們從 Spring 中獲取 UserPayService 的各個策略類,我們創建一個工廠類:
/** * @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); } }
這個 UserPayServiceStrategyFactory 中定義了一個 Map,用來保存所有的策略類的實例,并提供一個 getByUserType 方法,可以根據類型直接獲取對應的類的實例。還有一個 Register 方法,這個后面再講。
有了這個工廠類之后,計算價格的代碼即可得到大大的優化:
/** * @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 方法直接調用就可以了。
通過策略+工廠,我們的代碼很大程度的優化了,大大提升了可讀性和可維護性。
但是,上面還遺留了一個問題,那就是 UserPayServiceStrategyFactory 中用來保存所有的策略類的實例的 Map 是如何被初始化的?各個策略的實例對象如何塞進去的呢?
Spring Bean 的注冊
還記得我們前面定義的 UserPayServiceStrategyFactory 中提供了的 Register 方法嗎?他就是用來注冊策略服務的。
接下來,我們就想辦法調用 Register 方法,把 Spring 通過 IOC 創建出來的 Bean 注冊進去就行了。
這種需求,可以借用 Spring 中提供的 InitializingBean 接口,這個接口為 Bean 提供了屬性初始化后的處理方法。
它只包括 afterPropertiesSet 方法,凡是繼承該接口的類,在 Bean 的屬性初始化后都會執行該方法。
那么,我們將前面的各個策略類稍作改造即可:
/** * @author mhcoding */ @Service public class ParticularlyVipPayService implements UserPayService,InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { if (消費金額大于30元) { return 7折價格; } } @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折價格; } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register("SuperVip",this); } } @Service public class VipPayService implements UserPayService,InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { if(該用戶超級會員剛過期并且尚未使用過臨時折扣){ 臨時折扣使用次數更新(); returen 8折價格; } return 9折價格; } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register("Vip",this); } }
只需要每一個策略服務的實現類都實現 InitializingBean 接口,并實現其 afterPropertiesSet 方法,在這個方法中調用 UserPayServiceStrategyFactory.register 即可。
這樣,在 Spring 初始化的時候,當創建 VipPayService、SuperVipPayService 和 ParticularlyVipPayService 的時候,會在 Bean 的屬性初始化之后,把這個 Bean 注冊到 UserPayServiceStrategyFactory 中。
以上代碼,其實還是有一些重復代碼的,這里面還可以引入模板方法模式進一步精簡,這里就不展開了。
還有就是,UserPayServiceStrategyFactory.register 調用的時候,第一個參數需要傳一個字符串,這里的話其實也可以優化掉。
比如使用枚舉,或者在每個策略類中自定義一個 getUserType 方法,各自實現即可。
到此,相信大家對“怎么徹底干掉代碼中的if else”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。