實際項目中,往往有大量的if-else語句進行各種邏輯校驗,參數校驗等等,大量的if-else,語句使代碼變得臃腫且不好維護,本篇文章結合我自己的經驗,就減少if-else語句給出以下幾種方案,分別適用於不同的場景,供大家參考,如有疑問或者建議,請大家及時指出;
一. 方案一:使用三元表達式:
//使用if-else語句 String str; if (user.getAge()>18){ str="已成年"; }else { str="未成年"; } //使用三元表達式 str=user.getAge()>18?"成年":"未成年";
二. 方案二:使用JDK1.8中的Optional類包裝
//使用 if 語句 User user=userService.findById(userId); if (null==user){ throw new RuntimeException("參數錯誤,未找到指定用戶"); } //使用Optional類包裝 Optional.ofNullable(userService.findById(userId)).orElseThrow(()->new RuntimeException("參數錯誤,未找到指定用戶"));
使用Optional類的好處還在於在包裝成Optional容器后,可以使用函數式編程中的相關方法,例如filter(),map()方法,等等,用於篩選和轉換我們業務中的邏輯和對象,使得代碼得靈活性大大提高,例如:
//篩選出大於18歲的用戶,如果沒有就拋出異常 Optional.ofNullable(userService.findById(userId)) .filter(x->x.getAge()>18) .orElseThrow(()->new RuntimeException("參數錯誤,未找到指定用戶"));
代碼是否簡潔了很多呢
優點: 可以進行較為復雜的邏輯判斷
缺點: 條件判斷不宜過多,過多的條件判斷下不宜使用該方式
三.方案三:使用斷言Assert類
在Spring的org.springframework.util包中,內置了Assert斷言類,用於條件表達式的判斷
//使用斷言類 User user=userService.findById(userId); Assert.notNull(user, "參數錯誤,未找到指定用戶");
斷言類中的方法返回值是void,斷言類常用於我們做Junit單元測試,由於單元測試的方法均是無參數,無返回值的方法,因此Assert斷言類用於測試程序的返回值是否符合我們預期是再好不過了
優點:內置了很多判斷方法,例如 notNull,notEmpty,equal等方法,代碼可讀性強,相對方案一和方案二,可以適用於較多的判斷分支;
缺點:在斷言失敗時,異常只能是IllegalArgumentException(message),適用於較簡單的邏輯判斷
四.方案四:使用@Validate注解進行入參校驗的判斷
在企業開發中,進行表單驗證,以及接口的入參校驗時,往往會使用大量的if-else語句做參數校驗,這樣代碼會顯得特別臃腫和冗余,因此我們可以使用封裝好的庫來進行校驗,在JSR-303規范中,定義了參數校驗的注解@Valid,個大框架廠商例如spring,基於JSR303規范,提供了各自的實現,並且提供了很多高級的功能,例如@Validated就是@Valid的變體;
以下摘自org.springframework.validation.annotation中@Validated注解的文檔注釋
Variant of JSR-303's {@link javax.validation.Valid}, supporting the specification of validation groups. Designed for convenient use with Spring's JSR-303 support but not JSR-303 specific.
//接口定義 @RequestMapping("/update") public void updateUser(@RequestBody @Validated User user) { userService.updateUser(user); } //參數校驗 public class User implements Serializable { @NotNull(message = "參數不能為空") private Integer id; @NotBlank(message = "參數不能為空") private String username; @NotBlank(message = "參數不能為空") private String password; @NotEmpty(message = "參數不能為空") private List<String> desc; //這里可以通過正則來校驗時間格式是否正確 @NotNull(message = "參數不能為空") @Pattern(regexp = "xxxx",message ="時間格式不符合規范" ) private Date date; }
注意:如果@Validated參數校驗失敗,會拋出異常,如果需要在代碼中接收異常,可以在接口的參數中,添加參數BindingResult,添加了這個類,之后,異常就會被封裝到這個類中,不會向外拋出,我們可以調用這個類的API去獲取具體的異常信息,之后,我們可以根據異常信息,去定制化我們自己的響應
public ModelAndView save(@Validated CategoryForm form, BindingResult bindingResult, Map<String, Object> map) { if (bindingResult.hasErrors()) { map.put("msg", bindingResult.getFieldError().getDefaultMessage()); map.put("url", "/sell/seller/category/index"); return new ModelAndView("common/error", map); } }
優點: 非常適合在特定環境下做接口入參的校驗
缺點: 局限性大,無法在業務邏輯中使用
五.方案五:策略模式
策略模式是設計模式之一,設計模式的初衷是為了解決代碼中的特定問題而存在,百度一下策略模式的定義:
在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。在策略模式中,我們創建表示各種策略的對象和一個行為隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
簡單來說就是,算法(策略)和對象已經預先定義好,隨傳入參數的改變而選擇不同的算法(策略),更加具體的語義不就是根據不用的條件(if--else),選擇性的執行不同的代碼嗎? 本人在開發中也是屢次使用策略模式來重構復雜的if-else邏輯判斷,屢試不爽,大大提高代碼的優雅性;
下面是在企業開發中本人的例子,在Spring中如何使用策略模式 使用場景: 需求:多個接口,響應相同,根據參數傳入的類型,使用不同的策略;
//策略上下文 @Configuration public class StrategyContext{ @Resource public Map<String,Strategy> strategyMap; public List<Resp> doGet(String type){ Strategy strategy =strategyMap.get(type); retun strategy.doStrategy(); } } //配置策略 @Configuration public class StrategyConfig{ @Resource public ServiceImpl1 serviceImpl1 @Resource public ServiceImpl2 serviceImpl2 @Bean public Map<String,Strategy> getMap(){ Map<String,Strategy> strategyMap =new HashMap() strategyMap.put("1",new ServiceImpl1()); strategyMap.put("2",new ServiceImpl2()); return strategyMap } //策略接口類 public interface Strategy{ List<Resp> doStrategy(); } //具體策略1 public class ServiceImpl1 implements Strategy{ //重寫策略方法 @Override publicList<Resp> doStrategy(){ ... } } //具體策略2 public class ServiceImpl2 implements Strategy{ //重寫策略方法 @Override publicList<Resp> doStrategy(){ ... } } //在Controller層的代碼中注入策略上下文 public class AAAController{ @Autowired public StrategyContext context; public List<Resp> getXXX(String type){ //設計模式-策略模式 return context.doGet(type) } }
優點 :適合復雜的業務邏輯,代碼可擴展性強
缺點: 通常要配合工廠模式或者享元模式使用