還在用策略模式解決 if-else?Map+函數(shù)式接口方法才是YYDS!
本文介紹策略模式的具體應(yīng)用以及Map+函數(shù)式接口如何 「“更完美”」 的解決 if-else的問題。
需求
最近寫了一個(gè)服務(wù):根據(jù)優(yōu)惠券的類型resourceType和編碼resourceId來 查詢 發(fā)放方式grantType和領(lǐng)取規(guī)則
實(shí)現(xiàn)方式:
根據(jù)優(yōu)惠券類型resourceType -> 確定查詢哪個(gè)數(shù)據(jù)表 根據(jù)編碼resourceId -> 到對應(yīng)的數(shù)據(jù)表里邊查詢優(yōu)惠券的派發(fā)方式grantType和領(lǐng)取規(guī)則
優(yōu)惠券有多種類型,分別對應(yīng)了不同的數(shù)據(jù)庫表:
紅包 —— 紅包發(fā)放規(guī)則表 購物券 —— 購物券表 QQ會(huì)員 外賣會(huì)員
實(shí)際的優(yōu)惠券遠(yuǎn)不止這些,這個(gè)需求是要我們寫一個(gè)業(yè)務(wù)分派的邏輯
第一個(gè)能想到的思路就是if-else或者switch case:
switch(resourceType){
?case?"紅包":?
??查詢紅包的派發(fā)方式?
??break;
?case?"購物券":?
??查詢購物券的派發(fā)方式
??break;
?case?"QQ會(huì)員"?:
??break;
?case?"外賣會(huì)員"?:
??break;
?......
?default?:?logger.info("查找不到該優(yōu)惠券類型resourceType以及對應(yīng)的派發(fā)方式");
??break;
}
如果要這么寫的話, 一個(gè)方法的代碼可就太長了,影響了可讀性。(別看著上面case里面只有一句話,但實(shí)際情況是有很多行的)
而且由于 整個(gè) if-else的代碼有很多行,也不方便修改,可維護(hù)性低。
策略模式
策略模式是把 if語句里面的邏輯抽出來寫成一個(gè)類,如果要修改某個(gè)邏輯的話,僅修改一個(gè)具體的實(shí)現(xiàn)類的邏輯即可,可維護(hù)性會(huì)好不少。

「策略模式在業(yè)務(wù)邏輯分派的時(shí)候還是if-else」,只是說比第一種思路的if-else 更好維護(hù)一點(diǎn)。。。
switch(resourceType){
?case?"紅包":?
??String?grantType=new?Context(new?RedPaper()).ContextInterface();
??break;
?case?"購物券":?
??String?grantType=new?Context(new?Shopping()).ContextInterface();
??break;
?
?......
?default?:?logger.info("查找不到該優(yōu)惠券類型resourceType以及對應(yīng)的派發(fā)方式");
??break;
但缺點(diǎn)也明顯:
如果 if-else的判斷情況很多,那么對應(yīng)的具體策略實(shí)現(xiàn)類也會(huì)很多,上邊的具體的策略實(shí)現(xiàn)類還只是2個(gè),查詢紅包發(fā)放方式寫在類RedPaper里邊,購物券寫在另一個(gè)類Shopping里邊;那資源類型多個(gè)QQ會(huì)員和外賣會(huì)員,不就得再多寫兩個(gè)類?有點(diǎn)麻煩了 「沒法俯視整個(gè)分派的業(yè)務(wù)邏輯」
Map+函數(shù)式接口
用上了Java8的新特性lambda表達(dá)式
判斷條件放在key中 對應(yīng)的業(yè)務(wù)邏輯放在value中
這樣子寫的好處是非常直觀,能直接看到「判斷條件對應(yīng)的業(yè)務(wù)邏輯」
需求:根據(jù)優(yōu)惠券(資源)類型「resourceType」和編碼「resourceId」查詢派發(fā)方式「grantType」
上代碼:
@Service
public?class?QueryGrantTypeService?{
?
????@Autowired
????private?GrantTypeSerive?grantTypeSerive;
????private?Map>?grantTypeMap=new?HashMap<>();
????/**
?????*??初始化業(yè)務(wù)分派邏輯,代替了if-else部分
?????*??key:?優(yōu)惠券類型
?????*??value:?lambda表達(dá)式,最終會(huì)獲得該優(yōu)惠券的發(fā)放方式
?????*/
????@PostConstruct
????public?void?dispatcherInit(){
????????grantTypeMap.put("紅包",resourceId->grantTypeSerive.redPaper(resourceId));
????????grantTypeMap.put("購物券",resourceId->grantTypeSerive.shopping(resourceId));
????????grantTypeMap.put("qq會(huì)員",resourceId->grantTypeSerive.QQVip(resourceId));
????}
?
????public?String?getResult(String?resourceType){
????????//Controller根據(jù)?優(yōu)惠券類型resourceType、編碼resourceId?去查詢?發(fā)放方式grantType
????????Function?result=getGrantTypeMap.get(resourceType);
????????if(result!=null){
?????????//傳入resourceId?執(zhí)行這段表達(dá)式獲得String型的grantType
????????????return?result.apply(resourceId);
????????}
????????return?"查詢不到該優(yōu)惠券的發(fā)放方式";
????}
}
如果單個(gè) if 語句塊的業(yè)務(wù)邏輯有很多行的話,我們可以把這些 業(yè)務(wù)操作抽出來,寫成一個(gè)單獨(dú)的Service,即:
//具體的邏輯操作
@Service
public?class?GrantTypeSerive?{
????public?String?redPaper(String?resourceId){
????????//紅包的發(fā)放方式
????????return?"每周末9點(diǎn)發(fā)放";
????}
????public?String?shopping(String?resourceId){
????????//購物券的發(fā)放方式
????????return?"每周三9點(diǎn)發(fā)放";
????}
????public?String?QQVip(String?resourceId){
????????//qq會(huì)員的發(fā)放方式
????????return?"每周一0點(diǎn)開始秒殺";
????}
}
入?yún)?code style="overflow-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">String resourceId是用來查數(shù)據(jù)庫的,這里簡化了,傳參之后不做處理。
用http調(diào)用的結(jié)果:
@RestController
public?class?GrantTypeController?{
????@Autowired
????private?QueryGrantTypeService?queryGrantTypeService;
????@PostMapping("/grantType")
????public?String?test(String?resourceName){
????????return?queryGrantTypeService.getResult(resourceName);
????}
}

用Map+函數(shù)式接口也有弊端:
你的隊(duì)友得會(huì)lambda表達(dá)式才行啊,他不會(huì)讓他自己百度去
最后捋一捋本文講了什么:
策略模式通過接口、實(shí)現(xiàn)類、邏輯分派來完成,把 「if語句塊」的邏輯抽出來寫成「一個(gè)類」,更好維護(hù)。 Map+函數(shù)式接口通過 Map.get(key)來代替 if-else的業(yè)務(wù)分派,能夠避免策略模式帶來的類增多、難以俯視整個(gè)業(yè)務(wù)邏輯的問題。
