還在用策略模式解決 if-else?Map + 函數式接口就搞定了。。。


來源:https://blog.csdn.net/qq_44384533/article/details/109197926

本文介紹策略模式的具體應用以及Map+函數式接口如何 “更完美” 的解決 if-else的問題。

文章目錄

  • 需求
  • 策略模式
  • Map+函數式接口
  • 最后捋一捋本文講了什么

需求

最近寫了一個服務:根據優惠券的類型resourceType和編碼resourceId來 查詢 發放方式grantType和領取規則

實現方式:
  • 根據優惠券類型resourceType -> 確定查詢哪個數據表
  • 根據編碼resourceId -> 到對應的數據表里邊查詢優惠券的派發方式grantType和領取規則

優惠券有多種類型,分別對應了不同的數據庫表:

  • 紅包 —— 紅包發放規則表
  • 購物券 —— 購物券表
  • QQ會員
  • 外賣會員

實際的優惠券遠不止這些,這個需求是要我們寫一個業務分派的邏輯

第一個能想到的思路就是if-else或者switch case:

switch(resourceType){
 case "紅包": 
  查詢紅包的派發方式 
  break;
 case "購物券": 
  查詢購物券的派發方式
  break;
 case "QQ會員" :
  break;
 case "外賣會員" :
  break;
 ......
 default : logger.info("查找不到該優惠券類型resourceType以及對應的派發方式");
  break;
}

如果要這么寫的話, 一個方法的代碼可就太長了,影響了可讀性。(別看着上面case里面只有一句話,但實際情況是有很多行的)

而且由於 整個 if-else的代碼有很多行,也不方便修改,可維護性低。

策略模式

策略模式是把 if語句里面的邏輯抽出來寫成一個類,如果要修改某個邏輯的話,僅修改一個具體的實現類的邏輯即可,可維護性會好不少。

以下是策略模式的具體結構

策略模式在業務邏輯分派的時候還是if-else,只是說比第一種思路的if-else 更好維護一點。

switch(resourceType){
 case "紅包": 
  String grantType=new Context(new RedPaper()).ContextInterface();
  break;
 case "購物券": 
  String grantType=new Context(new Shopping()).ContextInterface();
  break;
 
 ......
 default : logger.info("查找不到該優惠券類型resourceType以及對應的派發方式");
  break;

但缺點也明顯:

  • 如果 if-else的判斷情況很多,那么對應的具體策略實現類也會很多,上邊的具體的策略實現類還只是2個,查詢紅包發放方式寫在類RedPaper里邊,購物券寫在另一個類Shopping里邊;那資源類型多個QQ會員和外賣會員,不就得再多寫兩個類?有點麻煩了
  • 沒法俯視整個分派的業務邏輯

Map+函數式接口

用上了Java8的新特性lambda表達式

  • 判斷條件放在key中
  • 對應的業務邏輯放在value中

這樣子寫的好處是非常直觀,能直接看到判斷條件對應的業務邏輯

需求:根據優惠券(資源)類型resourceType和編碼resourceId查詢派發方式grantType

上代碼:

@Service
public class QueryGrantTypeService {
 
    @Autowired
    private GrantTypeSerive grantTypeSerive;
    private Map<String, Function<String,String>> grantTypeMap=new HashMap<>();

    /**
     *  初始化業務分派邏輯,代替了if-else部分
     *  key: 優惠券類型
     *  value: lambda表達式,最終會獲得該優惠券的發放方式
     */
    @PostConstruct
    public void dispatcherInit(){
        grantTypeMap.put("紅包",resourceId->grantTypeSerive.redPaper(resourceId));
        grantTypeMap.put("購物券",resourceId->grantTypeSerive.shopping(resourceId));
        grantTypeMap.put("qq會員",resourceId->grantTypeSerive.QQVip(resourceId));
    }
 
    public String getResult(String resourceType){
        //Controller根據 優惠券類型resourceType、編碼resourceId 去查詢 發放方式grantType
        Function<String,String> result=getGrantTypeMap.get(resourceType);
        if(result!=null){
         //傳入resourceId 執行這段表達式獲得String型的grantType
            return result.apply(resourceId);
        }
        return "查詢不到該優惠券的發放方式";
    }
}

如果單個 if 語句塊的業務邏輯有很多行的話,我們可以把這些 業務操作抽出來,寫成一個單獨的Service,即:

//具體的邏輯操作

@Service
public class GrantTypeSerive {

    public String redPaper(String resourceId){
        //紅包的發放方式
        return "每周末9點發放";
    }
    public String shopping(String resourceId){
        //購物券的發放方式
        return "每周三9點發放";
    }
    public String QQVip(String resourceId){
        //qq會員的發放方式
        return "每周一0點開始秒殺";
    }
}

入參String resourceId是用來查數據庫的,這里簡化了,傳參之后不做處理。

用http調用的結果:

@RestController
public class GrantTypeController {

    @Autowired
    private QueryGrantTypeService queryGrantTypeService;

    @PostMapping("/grantType")
    public String test(String resourceName){
        return queryGrantTypeService.getResult(resourceName);
    }
}

用Map+函數式接口也有弊端:

  • 你的隊友得會lambda表達式才行啊,他不會讓他自己百度去

最后捋一捋本文講了什么

策略模式通過接口、實現類、邏輯分派來完成,把 if語句塊的邏輯抽出來寫成一個類,更好維護。

Map+函數式接口通過Map.get(key)來代替 if-else的業務分派,能夠避免策略模式帶來的類增多、難以俯視整個業務邏輯的問題。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.20w 程序員紅包封面,快快領取。。。

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM