如何優雅的替換掉Java代碼中的if else
場景
平時我們在寫代碼時,需要針對不同情況處理不同的業務邏輯,用得最多的就是if和else。 但是如果情況太多,就會出現一大堆的“if else”,這就是為什么很多遺留系統中,一個函數可能出現上千行的代碼。當然你說可以通過抽取方法或者類來實現,每一個情況交給一個方法或者對應一個類來處理,但是這樣做只是看起來代碼整潔了一些,還是有大量的”if else',后面有新的邏輯時,又要添加更多的“if else',沒有從根本上解決問題。
舉個例子,短信發送業務的實現,一般公司會接入多個短信供應商,比如夢網、玄武、阿里云等多個短信平臺(我們稱之為短信渠道),可能需要針對不同的短信類型或者短信平臺的穩定性來切換短信渠道:
比如阿里云短信管控很嚴,帶營銷字樣的短信不讓發送,則營銷類短信需要使用其他短信渠道來發送;也有可能某個短信平臺服務掛了暫時不可用,需要切換到另一個短信渠道;某些短信平臺有優惠,則需要臨時切換到該短信渠道發送短信;…
代碼實現
上面的業務場景簡單來說就是:針對不同的短信渠道來調用對應的短信平臺接口實現短信發送。短信渠道一般配置在文件中,或者配置在數據庫中。
代碼實現如下(注意下面所有的代碼都不能直接運行,只是關鍵邏輯部分的示例代碼):
爛代碼示例
我們有一個短信發送類:SmsSendService,里面有一個send方法發送短信
SmsSendService.java
public class SmsSendService{/** * @Param phoneNo 手機號 * @Param content 短信內容 */public void send(String phoneNo,String content){//從配置中讀取 短信渠道String channelType=config.getChannelType();//如果是短信渠道A,則調用渠道A的api發送if(Objects.equals(channelType,'CHANNEL_A')){System.out.println('通過短信渠道A發送短信');}//如果是短信渠道B,則調用渠道B的api發送else if(Objects.equals(channelType,'CHANNEL_B')){System.out.println('通過短信渠道B發送短信');}}}
如果某天增加了一個短信渠道C,那么接著追加一個”else if…'
//... 此處省略部分代碼 ...//從配置中讀取 短信渠道String channelType=config.getChannelType();//如果是短信渠道A,則調用渠道A的api發送if(Objects.equals(channelType,'CHANNEL_A')){System.out.println('通過短信渠道A發送短信');}//如果是短信渠道B,則調用渠道B的api發送else if(Objects.equals(channelType,'CHANNEL_B')){System.out.println('通過短信渠道B發送短信');}//ADD: 如果是短信渠道C,則調用渠道C的api發送else if(Objects.equals(channelType,'CHANNEL_C')){System.out.println('通過短信渠道C發送短信');}//... 此處省略部分代碼 ...
如果又加其他短信渠道了呢?你又寫一個“else if …' ?顯然這種做法不可取,也不符合SOLID原則中的”開閉原則“ ——對擴展開放,對更改封閉。這樣我們每次都需要修改原有代碼(對更改沒有封閉),不斷的添加”if else'。接下來我們把代碼優化一下:
優化代碼1
定義一個短信渠道的接口 SmsChannelService,所有的短信渠道API都實現該接口;
短信渠道接口 SmsChannelService.java
public interface SmsChannelService{//發送短信void send(String phoneNo,String content);}
短信渠道A SmsChannelServiceImplA.java
public class SmsChannelServiceImplA implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道A發送短信');}}
短信渠道B SmsChannelServiceImplB.java
public class SmsChannelServiceImplB implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道B發送短信');}}
通過工廠類來初始化所有短信渠道service
SmsChannelFactory.java
public class SmsChannelFactory {private Map<String,SmsChannelService> serviceMap;//初始化工廠,將所有的短信渠道Service放入Map中public SmsChannelFactory(){//渠道類型為 key , 對應的服務類為value :serviceMap=new HashMap<String, SmsChannelService>(2);serviceMap.put('CHANNEL_A',new SmsChannelServiceImplA());serviceMap.put('CHANNEL_B',new SmsChannelServiceImplB());}//根據短信渠道類型獲得對應渠道的Servicepublic SmsChannelService buildService(String channelType){return serviceMap.get(channelType);}}
在原來的SmsSendService中調用不同短信渠道的接口。原來的 SmsSendService 類優化如下
public class SmsSendService {private SmsChannelFactory smsChannelFactory;public SmsSendService(){smsChannelFactory=new SmsChannelFactory();}public void send(String phoneNo,String content){//從配置中讀取 短信渠道String channelType=config.getChannelType();//獲取渠道類型對應的服務類SmsChannelService channelService=smsChannelFactory.buildService(channelType);//發送短信channelService.send(phoneNo,content);}}
這樣SmsSendService類非常簡潔,把“if else'干掉了,如果我要增加一個短信渠道C,無需再次更改 SmsSendService 類。只需要增加一個類 SmsChannelServiceImplC 實現 SmsChannelService 接口,然后在工廠類 SmsChannelFactory 中增加一行初始化 SmsChannelServiceImplC 的代碼即可。
增加短信渠道C的實現 SmsChannelServiceImplC.java
public class SmsChannelServiceImplC implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道C發送短信');}}
修改工廠類 SmsChannelFactory.java
public class SmsChannelFactory {private Map<String,SmsChannelService> serviceMap;//初始化 serviceMap ,將所有的短信渠道Service放入Map中public SmsChannelFactory(){//渠道類型為 key , 對應的服務類為value :serviceMap=new HashMap<String, SmsChannelService>(3);serviceMap.put('CHANNEL_A',new SmsChannelServiceImplA());serviceMap.put('CHANNEL_B',new SmsChannelServiceImplB());//ADD 增加一行 SmsChannelServiceImplC 的初始化代碼 serviceMap.put('CHANNEL_C',new SmsChannelServiceImplC());}//根據渠道類型構建短信渠道Servicepublic SmsChannelService buildService(String channelType){return serviceMap.get(channelType);}}
“if else'是干掉了,但還是得修改原來的類 SmsChannelFactory ,不滿足'開閉原則',有沒有更好得方式呢?
我們通過使用spring的依賴注入進一步優化代碼:
優化代碼2
SmsChannelService 接口增加 getChannelType() 方法,這一步很關鍵。
public interface SmsChannelService {//發送短信void send(String phoneNo,String content);//關鍵:增加getChannelType()方法,子類實現這個方法用于標識出渠道類型String getChannelType();}
子類增加該方法的實現,并加上 @Service 注解,使其讓spring容器管理起來
SmsChannelServiceImplA.java
@Servicepublic class SmsChannelServiceImplA implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道A發送短信');}//關鍵:增加 getChannelType() 實現public String getChannelType() {return 'CHANNEL_A';}}
SmsChannelServiceImplB.java
@Servicepublic class SmsChannelServiceImplB implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道B發送短信');}//關鍵:增加 getChannelType() 實現public String getChannelType() {return 'CHANNEL_B';}}
修改 SmsChannelFactory 類: 這一步也很關鍵。
SmsChannelFactory.java
@Servicepublic class SmsChannelFactory {private Map<String,SmsChannelService> serviceMap;/*注入:通過spring容器將所有實現 SmsChannelService 接口的類的實例注入到 serviceList 中*/@Autowiredprivate List<SmsChannelService> serviceList;/*通過 @PostConstruct 注解,在 SmsChannelFactory 實例化后,來初始化 serviceMap */@PostConstructprivate void init(){if(CollectionUtils.isEmpty(serviceList)){return ;}serviceMap=new HashMap<String, SmsChannelService>(serviceList.size());//將 serviceList 轉換為 serviceMapfor (SmsChannelService channelService : serviceList) {String channelType=channelService.getChannelType();//重復性校驗,避免不同實現類的 getChannelType() 方法返回同一個值。if(serviceMap.get(channelType)!=null){throw new RuntimeException('同一個短信渠道只能有一個實現類');}/*渠道類型為 key , 對應的服務類為value :與“優化代碼1”中的通過手工設置“CHANNEL_A'、'CHANNEL_B'相比,這種方式更加自動化,后續在增加“CHANNEL_C'無需再改此處代碼*/serviceMap.put(channelType,channelService);}}//根據渠道類型獲取對應短信渠道的Servicepublic SmsChannelService buildService(String channelType){return serviceMap.get(channelType);}}
SmsSendService 加上 @Service 注解。通過 @Autowired 注入 SmsChannelFactory
SmsSendService.java
@Servicepublic class SmsSendService {@Autowiredprivate SmsChannelFactory smsChannelFactory;public void send(String phoneNo,String content){//從配置中讀取短信渠道類型String channelType=config.getChannelType();//構建渠道類型對應的服務類SmsChannelService channelService=smsChannelFactory.buildService(channelType);//發送短信channelService.send(phoneNo,content);}}
這時,如果需要添加一個渠道C,那真的只需要添加一個 SmsChannelServiceImplC 即可,再也不用改原有代碼,完全遵循“開閉原則”。
SmsChannelServiceImplC.java
@Servicepublic class SmsChannelServiceImplC implements SmsChannelService {public void send(String phoneNo, String content) {System.out.println('通過短信渠道C發送短信');}public String getChannelType() {return 'CHANNEL_C';}}
以上就是如何優雅的替換掉Java代碼中的if else的詳細內容,更多關于替換代碼中的if else的資料請關注好吧啦網其它相關文章!
相關文章:
