責(zé)任鏈模式——更靈活的 if else
責(zé)任鏈,顧名思義,就是用來處理相關(guān)事務(wù)責(zé)任的一條執(zhí)行鏈,執(zhí)行鏈上有多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都有機(jī)會(huì)(條件匹配)處理請(qǐng)求事務(wù),如果某個(gè)節(jié)點(diǎn)處理完了就可以根據(jù)實(shí)際業(yè)務(wù)需求傳遞給下一個(gè)節(jié)點(diǎn)繼續(xù)處理或者返回處理完畢。這種模式給予請(qǐng)求的類型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。屬于行為型模式。
在這種模式中,通常每個(gè)接收者都包含對(duì)另一個(gè)接收者的引用。如果一個(gè)對(duì)象不能處理該請(qǐng)求,那么它會(huì)把相同的請(qǐng)求傳給下一個(gè)接收者,依此類推。

先來看一段代碼
public?void?test(int?i,?Request?request){
??if(i==1){
????Handler1.response(request);
??}else?if(i?==?2){
????Handler2.response(request);
??}else?if(i?==?3){
????Handler3.response(request);
??}else?if(i?==?4){
????Handler4.response(request);
??}else{
????Handler5.response(request);
??}
}
代碼的業(yè)務(wù)邏輯是這樣的,方法有兩個(gè)參數(shù):整數(shù) i 和一個(gè)請(qǐng)求 request,根據(jù) i 的值來決定由誰來處理 request,如果 i==1,由 Handler1來處理,如果 i==2,由 Handler2 來處理,以此類推。在編程中,這種處理業(yè)務(wù)的方法非常常見,所有處理請(qǐng)求的類由if…else…條件判斷語句連成一條責(zé)任鏈來對(duì)請(qǐng)求進(jìn)行處理,相信大家都經(jīng)常用到。這種方法的優(yōu)點(diǎn)是非常直觀,簡單明了,并且比較容易維護(hù),但是這種方法也存在著幾個(gè)比較令人頭疼的問題:
代碼臃腫:實(shí)際應(yīng)用中的判定條件通常不是這么簡單地判斷是否為1或者是否為2,也許需要復(fù)雜的計(jì)算,也許需要查詢數(shù)據(jù)庫等等,這就會(huì)有很多額外的代碼,如果判斷條件再比較多,那么這個(gè)if…else…語句基本上就沒法看了。 耦合度高:如果我們想繼續(xù)添加處理請(qǐng)求的類,那么就要繼續(xù)添加if…else…判定條件;另外,這個(gè)條件判定的順序也是寫死的,如果想改變順序,那么也只能修改這個(gè)條件語句。
既然缺點(diǎn)我們已經(jīng)清楚了,就要想辦法來解決。這個(gè)場(chǎng)景的業(yè)務(wù)邏輯很簡單:如果滿足條件1,則由 Handler1 來處理,不滿足則向下傳遞;如果滿足條件2,則由 Handler2 來處理,不滿足則繼續(xù)向下傳遞,以此類推,直到條件結(jié)束。其實(shí)改進(jìn)的方法也很簡單,就是把判定條件的部分放到處理類中,這就是責(zé)任連模式的原理。
定義
責(zé)任鏈模式(Chain of Responsibility Pattern):使多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求,從而避免了請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系。將這些對(duì)象連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求,直到有對(duì)象處理它為止。
角色
Handler:抽象處理類,抽象處理類中主要包含一個(gè)指向下一處理類的成員變量nextHandler和一個(gè)處理請(qǐng)求的方法handRequest,handRequest方法的主要主要思想是,如果滿足處理的條件,則有本處理類來進(jìn)行處理,否則由nextHandler來處理 ConcreteHandler:具體處理類主要是對(duì)具體的處理邏輯和處理的適用條件進(jìn)行實(shí)現(xiàn)。具體處理者接到請(qǐng)求后,可以選擇將請(qǐng)求處理掉,或者將請(qǐng)求傳給下家。由于具體處理者持有對(duì)下家的引用,因此,如果需要,具體處理者可以訪問下家 Client:客戶端
類圖

coding
public?abstract?class?Handler?{
????private?Handler?nextHandler;
????private?int?level;
????public?Handler(int?level)?{
????????this.level?=?level;
????}
????public?void?setNextHandler(Handler?handler){
????????this.nextHandler?=?handler;
????}
????public?final?void?handlerRequest(Request?request){
????????if(level?==?request.getLevel()){
????????????this.response(request);
????????}else{
????????????if?(this.nextHandler?!=?null){
????????????????this.nextHandler.handlerRequest(request);
????????????}else{
????????????????System.out.println("===已經(jīng)沒有處理器了===");
????????????}
????????}
????}
????//?抽象方法,子類實(shí)現(xiàn)
????public?abstract?void?response(Request?request);
}
class?Request?{
????int?level?=?0;
????public?Request(int?level){
????????this.level?=?level;
????}
????public?int?getLevel()?{
????????return?level;
????}
}
public?class?ConcreteHandler1?extends?Handler?{
????public?ConcreteHandler1(int?level)?{
????????super(level);
????}
????@Override
????public?void?response(Request?request)?{
????????System.out.println("請(qǐng)求由處理器1進(jìn)行處理");
????}
}
public?class?ConcreteHandler2?extends?Handler?{
?//...
}
public?class?ConcreteHandler2?extends?Handler?{
?//...
}
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????ConcreteHandler1?handler1?=?new?ConcreteHandler1(1);
????????ConcreteHandler2?handler2?=?new?ConcreteHandler2(2);
????????ConcreteHandler3?handler3?=?new?ConcreteHandler3(3);
????//處理者構(gòu)成一個(gè)環(huán)形
????????handler1.setNextHandler(handler2);
????????handler2.setNextHandler(handler3);
????????handler1.handlerRequest(new?Request(1));
????}
}
實(shí)例
當(dāng)你想要讓一個(gè)以上的對(duì)象有機(jī)會(huì)能夠處理某個(gè)請(qǐng)求的時(shí)候,就是用責(zé)任鏈模式。
通過責(zé)任鏈模式,你可以為某個(gè)請(qǐng)求創(chuàng)建一個(gè)對(duì)象鏈。每個(gè)對(duì)象依序檢查此請(qǐng)求,并對(duì)其進(jìn)行處理,或者將它傳給鏈中的下一個(gè)對(duì)象。
比如
程序員要請(qǐng)3天以上的假期,在OA申請(qǐng),需要直接主管、總監(jiān)、HR 層層審批后才生效。類似的采購審批、報(bào)銷審批。。。 美團(tuán)在外賣營銷業(yè)務(wù)中資源位展示的邏輯:設(shè)計(jì)模式在外賣營銷業(yè)務(wù)中的實(shí)踐
應(yīng)用
JAVA 中的異常處理機(jī)制、JAVA WEB 中 Apache Tomcat 對(duì) Encoding 的處理,Struts2 的攔截器,JSP、Servlet 的 Filter 均是責(zé)任鏈的典型應(yīng)用。
Servlet 中的責(zé)任鏈
public?final?class?ApplicationFilterChain?implements?FilterChain?{
????private?static?final?ThreadLocal?lastServicedRequest;
????private?static?final?ThreadLocal?lastServicedResponse;
????public?static?final?int?INCREMENT?=?10;
????private?ApplicationFilterConfig[]?filters?=?new?ApplicationFilterConfig[0];
????private?int?pos?=?0;??//下一個(gè)要執(zhí)行的filter的位置
????private?int?n?=?0;????//filter個(gè)數(shù)
????private?Servlet?servlet?=?null;
????public?ApplicationFilterChain()?{
????}
????public?void?doFilter(ServletRequest?request,?ServletResponse?response)?throws?IOException,?ServletException?{
????????if?(Globals.IS_SECURITY_ENABLED)?{
????????????final?ServletRequest?req?=?request;
????????????final?ServletResponse?res?=?response;
????????????try?{
????????????????AccessController.doPrivileged(new?PrivilegedExceptionAction()?{
????????????????????public?Void?run()?throws?ServletException,?IOException?{
????????????????????????ApplicationFilterChain.this.internalDoFilter(req,?res);
????????????????????????return?null;
????????????????????}
????????????????});
????????????}?catch?(PrivilegedActionException?var7)?{
????????????????Exception?e?=?var7.getException();
????????????????if?(e?instanceof?ServletException)?{
????????????????????throw?(ServletException)e;
????????????????}
????????????????if?(e?instanceof?IOException)?{
????????????????????throw?(IOException)e;
????????????????}
????????????????if?(e?instanceof?RuntimeException)?{
????????????????????throw?(RuntimeException)e;
????????????????}
????????????????throw?new?ServletException(e.getMessage(),?e);
????????????}
????????}?else?{
????????????this.internalDoFilter(request,?response);
????????}
????}
FilterChain 就是一條過濾鏈。其中每個(gè)過濾器(Filter)都可以決定是否執(zhí)行下一步。過濾分兩個(gè)方向,進(jìn)和出:
進(jìn):在把ServletRequest和ServletResponse交給Servlet的service方法之前,需要進(jìn)行過濾
出:在service方法完成后,往客戶端發(fā)送之前,需要進(jìn)行過濾
Spring MVC 中的責(zé)任鏈
Spring MVC 的 diapatcherServlet 的 doDispatch 方法中,獲取與請(qǐng)求匹配的處理器 HandlerExecutionChain就是用到了責(zé)任鏈模式。
protected?void?doDispatch(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception?{
????????HttpServletRequest?processedRequest?=?request;
????????HandlerExecutionChain?mappedHandler?=?null;????//使用到了責(zé)任鏈模式
????????boolean?multipartRequestParsed?=?false;
????????WebAsyncManager?asyncManager?=?WebAsyncUtils.getAsyncManager(request);
????????try?{
????????????try?{
????????????????ModelAndView?mv?=?null;
????????????????Object?dispatchException?=?null;
????????????????try?{
????????????????????processedRequest?=?this.checkMultipart(request);
????????????????????multipartRequestParsed?=?processedRequest?!=?request;
????????????????????mappedHandler?=?this.getHandler(processedRequest);?
????????????????????if?(mappedHandler?==?null)?{
????????????????????????this.noHandlerFound(processedRequest,?response);
????????????????????????return;
????????????????????}
????????????????????HandlerAdapter?ha?=?this.getHandlerAdapter(mappedHandler.getHandler());
????????????????????String?method?=?request.getMethod();
????????????????????boolean?isGet?=?"GET".equals(method);
????????????????????if?(isGet?||?"HEAD".equals(method))?{
????????????????????????long?lastModified?=?ha.getLastModified(request,?mappedHandler.getHandler());
????????????????????????if?((new?ServletWebRequest(request,?response)).checkNotModified(lastModified)?&&?isGet)?{
????????????????????????????return;
????????????????????????}
????????????????????}
??????????//責(zé)任鏈模式執(zhí)行預(yù)處理方法,其實(shí)是將請(qǐng)求交給注冊(cè)的攔截器執(zhí)行
????????????????????if?(!mappedHandler.applyPreHandle(processedRequest,?response))?{
????????????????????????return;
????????????????????}
????????????????????mv?=?ha.handle(processedRequest,?response,?mappedHandler.getHandler());
????????????????????if?(asyncManager.isConcurrentHandlingStarted())?{
????????????????????????return;
????????????????????}
????????????????????this.applyDefaultViewName(processedRequest,?mv);
???????????????????//責(zé)任鏈執(zhí)行后處理方法
????????????????????mappedHandler.applyPostHandle(processedRequest,?response,?mv);
????????????????}?catch?(Exception?var22)?{
?????????????//...
????????}?finally?{
?????}
?}
SpringMVC 請(qǐng)求的流程中,執(zhí)行了攔截器相關(guān)方法 interceptor.preHandler 等等 在處理 SpringMVC 請(qǐng)求時(shí),使用到職責(zé)鏈模式還使用到適配器模式 HandlerExecutionChain 主要負(fù)責(zé)的是請(qǐng)求攔截器的執(zhí)行和請(qǐng)求處理,但是他本身不處理請(qǐng)求,只是將請(qǐng)求分配給鏈上注冊(cè)處理器執(zhí)行,這是職責(zé)鏈實(shí)現(xiàn)方式,減少職責(zé)鏈本身與處理邏輯之間的耦合,規(guī)范了處理流程 HandlerExecutionChain 維護(hù)了 HandlerInterceptor 的集合, 可以向其中注冊(cè)相應(yīng)的攔截器
總結(jié)
責(zé)任鏈模式其實(shí)就是一個(gè)靈活版的 if…else…語句,它就是將這些判定條件的語句放到了各個(gè)處理類中,這樣做的優(yōu)點(diǎn)是比較靈活了,但同樣也帶來了風(fēng)險(xiǎn),比如設(shè)置處理類前后關(guān)系時(shí),一定要特別仔細(xì),搞對(duì)處理類前后邏輯的條件判斷關(guān)系,并且注意不要在鏈中出現(xiàn)循環(huán)引用的問題。
優(yōu)點(diǎn):
降低耦合度:將請(qǐng)求和處理分開,實(shí)現(xiàn)解耦,提高了系統(tǒng)的靈活性。 簡化了對(duì)象:對(duì)象不需要知道鏈的結(jié)構(gòu) 良好的擴(kuò)展性:增加處理者的實(shí)現(xiàn)很簡單,只需重寫處理請(qǐng)求業(yè)務(wù)邏輯的方法。
缺點(diǎn):
從鏈頭發(fā)出,直到有處理者響應(yīng),在責(zé)任鏈比較長的時(shí)候會(huì)影響系統(tǒng)性能,一般需要在 Handler 中設(shè)置一個(gè)最大節(jié)點(diǎn)數(shù)。 請(qǐng)求遞歸,調(diào)試排錯(cuò)比較麻煩。
使用場(chǎng)景:
有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求由運(yùn)行時(shí)刻自動(dòng)確定。 在不明確指定接收者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。 可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求。
模式的擴(kuò)展:
職責(zé)鏈模式存在以下兩種情況。
純的職責(zé)鏈模式:一個(gè)請(qǐng)求必須被某一個(gè)處理者對(duì)象所接收,且一個(gè)具體處理者對(duì)某個(gè)請(qǐng)求的處理只能采用以下兩種行為之一:自己處理(承擔(dān)責(zé)任);把責(zé)任推給下家處理。 不純的職責(zé)鏈模式:允許出現(xiàn)某一個(gè)具體處理者對(duì)象在承擔(dān)了請(qǐng)求的一部分責(zé)任后又將剩余的責(zé)任傳給下家的情況,且一個(gè)請(qǐng)求可以最終不被任何接收端對(duì)象所接收。
