三歪手把手教你干掉if else
今天想來跟大家討論一下怎么干掉if else。
已經(jīng)工作的人可能深有體會(huì):沒有什么是if else搞不掂的,如果有,那就再嵌套一層。
大多數(shù)人都是做業(yè)務(wù)開發(fā)的,if else是避免不了的,但怎么讓if else的邏輯看起來更順眼,變得更加好看,更加好維護(hù)呢?
如果之前看過三歪文章的同學(xué)可能就會(huì)想到「責(zé)任鏈模式」。
沒錯(cuò)就是 責(zé)任鏈模式
當(dāng)你看到一個(gè)Service中有一大堆if else 邏輯的時(shí)候,可能你會(huì)幻想著要不要重構(gòu)掉,但是始終下不了手。
所以,今天想來分享一個(gè)「通用」的責(zé)任鏈模式的模板,把if else給套進(jìn)去就完事了,我相信都能學(xué)會(huì)。
之前寫設(shè)計(jì)模式文章的時(shí)候,有的同學(xué)會(huì)評(píng)論說我把東西搞復(fù)雜了,本來就有簡(jiǎn)單的方式去弄,為啥就要嵌套這么多層去搞這些花里胡哨的東西。
在我看來,用最簡(jiǎn)單的方式去實(shí)現(xiàn)是沒有任何問題的。但達(dá)到一定代碼量的時(shí)候,多想想一下,換一個(gè)人去維護(hù),人家能不能看懂,有沒有更加好的方式,這往往就需要「抽象」的能力。
這也是為什么這么多人推崇設(shè)計(jì)模式的原因。
不多BB,來吧。
責(zé)任鏈通用實(shí)現(xiàn)
現(xiàn)在我就默認(rèn)大家都知道什么是責(zé)任鏈模式了,如果還對(duì)這個(gè)不懂的同學(xué),可以先看看我之前的文章。

首先,我們會(huì)有一個(gè)業(yè)務(wù)執(zhí)行器接口,所有的業(yè)務(wù)實(shí)現(xiàn)都會(huì)實(shí)現(xiàn)該接口,這意味著上圖的邏輯A、B、C都會(huì)實(shí)現(xiàn)這個(gè)接口
/**
?*?業(yè)務(wù)執(zhí)行器
?*?@author?三歪
?*/
public?interface?BusinessProcess?{
????void?process(ProcessContext?context);
}
可以看到的是接口異常的簡(jiǎn)單,只有一個(gè)process處理的方法,方法接收的是ProcessContext
為什么process方法需要接收ProcessContext?很簡(jiǎn)單,我們?cè)谔幚?code style="font-size:14px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">邏輯A、B、C的時(shí)候,可能邏輯B需要依賴邏輯A的處理結(jié)果。于是我們就需要有一個(gè)載體把這些給記錄下來。
所以,我們就有了ProcessContext,它代表的是責(zé)任鏈的上下文。
/**
?*?責(zé)任鏈上下文
?*?@author?3y
?*/
public?class?ProcessContext?{
????//?標(biāo)識(shí)責(zé)任鏈的code
????private?String?code;
????//?存儲(chǔ)上下文的真正載體
????private?Model?model;
????//?責(zé)任鏈中斷的標(biāo)識(shí)
????private?Boolean?needBreak?=?false;
}
現(xiàn)在責(zé)任鏈的執(zhí)行器和責(zé)任鏈所涉及的上下文都已經(jīng)有了,這意味著我們已經(jīng)有了責(zé)任鏈最主要的抽象了。
接下來就是我們需要把鏈給串起來,于是我們需要一個(gè)模板,其實(shí)我們做的就是用一個(gè)List來把BusinessProcess的子類給串起來。
/**
?*?業(yè)務(wù)執(zhí)行模板(把責(zé)任鏈的邏輯串起來)
?*?@author?3y
?*/
public?class?ProcessTemplate?{
????private?List?processList;
????public?List?getProcessList()? {
????????return?processList;
????}
????public?void?setProcessList(List?processList) ?{
????????this.processList?=?processList;
????}
}
OK,現(xiàn)在我們已經(jīng)把責(zé)任鏈的整塊給抽象好了,接下來就是暴露流程控制器去執(zhí)行這個(gè)責(zé)任鏈:
/**
?*?責(zé)任鏈的流程控制器(整個(gè)責(zé)任鏈的執(zhí)行流程通用控制)
?*?@author?3y?
?*/
@Data
public?class?ProcessController?{
????
????//?不同的code?對(duì)應(yīng)不同的責(zé)任鏈
????private?Map?templateConfig?=?null;
????public?void?process(ProcessContext?context)?{
????????//根據(jù)上下文的Code?執(zhí)行不同的責(zé)任鏈
????????String?businessCode?=?context.getCode();
????????ProcessTemplate?processTemplate?=?templateConfig.get(businessCode);
????????List?actionList?=?processTemplate.getProcessList();
????????//遍歷某個(gè)責(zé)任鏈的流程節(jié)點(diǎn)
????????for?(BusinessProcess?action?:?actionList)?{
????????????try?{
????????????????action.process(context);
????????????????if?(context.getNeedBreak())?{
????????????????????break;
????????????????}
????????????}?catch?(Exception?e2)?{
????????????????//...
????????????}
????????}
????}
}
我們可以看到的是在ProcessController執(zhí)行鏈通用的流程控制器上會(huì)有一個(gè)Map去存儲(chǔ)多個(gè)責(zé)任鏈的模板,這樣做的好處就是:ProcessController這個(gè)流程控制器可以根據(jù)code支持多個(gè)責(zé)任鏈執(zhí)行。
接下來就是我們有具體的BusinessProcess去加入到ProcessTemplate的鏈上,然后調(diào)用ProcessController的方法去執(zhí)行整一條推送鏈。
一般我們?cè)?code style="font-size:14px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;">XML注入就好了,比如說現(xiàn)在我們有兩個(gè)BusinessProcess的實(shí)現(xiàn),分別是白名單和發(fā)消息的邏輯:
/**
?*?白名單處理器
?*?@author?3y
?*/
@Service
public?class?WhiteListProcess?implements?BusinessProcess?{
????@Override
????public?void?process(ProcessContext?context)?{
????????UserModel?user?=?(UserModel)?context.getModel();
????????if?("3y".equals(user.getName()))?{
????????????context.setNeedBreak(true);
????????}
????}
}
/**
?*?發(fā)消息處理器
?*?@author?三歪
?*/
@Service
public?class?SendMessageProcess?implements?BusinessProcess?{
????@Override
????public?void?process(ProcessContext?context)?{
????????UserModel?user?=?(UserModel)?context.getModel();
????????System.out.println("給"+user.getName()+"發(fā)消息");
????}
}
然后我們把上面兩個(gè)處理器添加到ProcessTemplate的模板上,把ProcessTemplate添加到ProcessController的Map上:
<bean?id="sendMessageTemplate"?class="com.chainofresponsibility.ProcessTemplate">
??<property?name="processList">
????<list>
??????<ref?bean="whiteListProcess">ref>
??????<ref?bean="sendMessageProcess">ref>
????list>
??property>
bean>
<bean?id="processController"?class="com.chainofresponsibility.ProcessController">
??<property?name="templateConfig">
????<map>
??????<entry?key="sendMessage"?value-ref="sendMessageTemplate"?/>
????map>
??property>
bean>
然后我們?cè)诮涌诶镞厛?zhí)行這個(gè)責(zé)任鏈:
@RestController
public?class?UserController?{
????@Autowired
????private?ProcessController?processController;
????@RequestMapping("/send")
????public?void??send(String?userName)?{
????????//?構(gòu)建上下文
????????ProcessContext?processContext?=?new?ProcessContext();
????????UserModel?userModel?=?new?UserModel();
????????userModel.setAge("24");
????????userModel.setName(userName);
????????processContext.setModel(userModel);
????????processContext.setCode("sendMessage");
????????processController.process(processContext);
????}
}
我做了這么大的一套東西實(shí)現(xiàn)了什么功能?其實(shí)就一個(gè)if邏輯:
if?("3y".equals(userModel.getName()))?{
??return;
}
System.out.println("給"?+?userModel.getName()?+?"發(fā)消息");
下面我們還是來看看效果,從功能上我們可以發(fā)現(xiàn),只要我們輸入的不是「3y」,那就會(huì)打印消息

上面的邏輯,實(shí)際上就是一套通用的責(zé)任鏈的代碼,最核心的其實(shí)就是四個(gè)角色:「業(yè)務(wù)抽象接口」、「執(zhí)行過程中的上下文」、「將業(yè)務(wù)實(shí)現(xiàn)類串起來」和「一個(gè)通用的控制器執(zhí)行責(zé)任鏈」

如果沒看懂的同學(xué),三歪建議再對(duì)比一下代碼看看,責(zé)任鏈這種設(shè)計(jì)模式是非常好用,在項(xiàng)目里邊也是非常常見的。
只要把BusinessProcess/ProcessContext/ProcessTemplate/ProcessController的代碼給拷過去自己的項(xiàng)目中,這就能幫你把原有的if else邏輯給干掉。
Pipeline
不知道大家看過Pipeline這個(gè)詞了沒,在學(xué)Redis的時(shí)候可能會(huì)見過,在Redis里邊我們會(huì)用Pipeline去做批量的操作。
拋開Redis的Pipeline,但從宏觀的角度上來,Pipeline其實(shí)是一種架構(gòu)思想。
同時(shí)我也認(rèn)為它是「責(zé)任鏈模式」的實(shí)現(xiàn)之一。
下面來看看我這邊的一個(gè)Pipeline實(shí)現(xiàn)的架構(gòu)圖:

可以看到前人實(shí)現(xiàn)的Pipepline還是相對(duì)復(fù)雜的,沒有上面通用的責(zé)任鏈模式好理解,經(jīng)過分析可以看到都是換湯不換藥的。
下次再見到Pipeline這個(gè)詞的時(shí)候(因?yàn)檫@個(gè)詞還是很常見的),你們就應(yīng)該能想到責(zé)任鏈模式,然后你就發(fā)現(xiàn)你看懂了。
代碼GitHub:https://github.com/ZhongFuCheng3y/Java3yTestReposity
各類知識(shí)點(diǎn)總結(jié)
下面的文章都有對(duì)應(yīng)的原創(chuàng)精美PDF,在持續(xù)更新中,可以來找我催更~
- 92頁的Mybatis
- 129頁的多線程
- 141頁的Servlet
- 158頁的JSP
- 76頁的集合
- 64頁的JDBC
- 105頁的數(shù)據(jù)結(jié)構(gòu)和算法
- 142頁的Spring
- 58頁的過濾器和監(jiān)聽器
- 30頁的HTTP
- 42頁的SpringMVC
- Hibernate
- AJAX
- Redis
- ......
掃碼或者微信搜Java3y?免費(fèi)領(lǐng)取原創(chuàng)思維導(dǎo)圖、精美PDF。在公眾號(hào)回復(fù)「888」領(lǐng)取,PDF內(nèi)容純手打有任何不懂歡迎來問我。
原創(chuàng)電子書
原創(chuàng)思維導(dǎo)圖

![]() |
|


