流行框架源碼分析(13)-責(zé)任鏈設(shè)計(jì)模式

主目錄見:Android高級進(jìn)階知識(shí)(這是總目錄索引)
?學(xué)習(xí)設(shè)計(jì)模式,個(gè)人覺得第一就是不能局限于一個(gè)思維,也許些許的變化有時(shí)能讓它更加符合你的設(shè)計(jì),所以我們不要拘泥于固定的角色,生搬硬套的場景,我們要經(jīng)得起變化。這也是為什么會(huì)有那么多的變形的原因。我們今天要講責(zé)任鏈設(shè)計(jì)模式(chain of responsibility),這個(gè)模式也是會(huì)有很多的變形,我們待會(huì)會(huì)在分析例子的過程中講到。我們先來看下它的定義:

責(zé)任鏈模式是一種對象的行為模式。在責(zé)任鏈模式里,很多對象由每一個(gè)對象對其下家的引用而連接起來形成一條鏈。請求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對象決定處理此請求。發(fā)出這個(gè)請求的客戶端并不知道鏈上的哪一個(gè)對象最終處理這個(gè)請求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織和分配責(zé)任。

這個(gè)定義非常明確地說明了責(zé)任鏈模式的作用,很多對象由每一個(gè)對象對其下家的引用而連接起來形成一條鏈也就是說請求向上傳遞過程中,如果自己不處理的話,那么由于自己持有了下家的引用,所以可以交給下家去處理,這樣循環(huán)下去直到有對象想要處理。我們接著來看看UML類圖:


責(zé)任鏈設(shè)計(jì)模式

角色介紹:

  • Handler:抽象處理者,聲明一個(gè)請求的處理方法,并保持對下一個(gè)處理節(jié)點(diǎn)Handler對象的引用。

  • ConcreteHandler:具體處理者,對請求進(jìn)行處理,如果不處理就講請求轉(zhuǎn)發(fā)給下一個(gè)節(jié)點(diǎn)上的處理對象。

一.目標(biāo)

講這個(gè)設(shè)計(jì)模式,是覺得他用的地方還是蠻廣的,而且我們最熟悉的事件分發(fā)應(yīng)該就是典型的責(zé)任鏈設(shè)計(jì)模式吧,所以我們今天目標(biāo)也是:
1.明白責(zé)任鏈設(shè)計(jì)模式怎么使用;
2.知道在什么場景需要用到責(zé)任鏈設(shè)計(jì)模式。

二.模式分析

同樣地,我們今天也先假設(shè)一個(gè)場景,公司有一天安排你去出差,那你說我要申請費(fèi)用500塊,然后你申請表就提交給組長,組長說數(shù)額太大不歸我管,讓主管來吧,主管也說這個(gè)我也不能管,讓經(jīng)理來吧,經(jīng)理一甩手,允許了,然后你就拿著錢屁顛屁顛出差去了。這里我們首先看看我們抽象處理者角色:

public abstract class Handler {
    protected int expenses;
    protected Handler mNextChain;
    
    public Handler(int expenses){
        this.expenses = expenses;
    }
    
    public void setNextHandler(Handler handler){
        this.mNextChain = handler;
    }
    
    public abstract void handleRequest(Request request);
}

我們看到這是一個(gè)典型的抽象處理者角色,里面有設(shè)置下家的引用setNextHandler(),還有處理?xiàng)l件expenses,處理方法handleRequest()。然后我們分別來看幾個(gè)具體的處理者角色首先是組長:

public class TeamLeader extends Handler{
    
    public TeamLeader() {
        super(200);
    }

    @Override
    public void handleRequest(Request request) {
        
        if (request.getApplyExpense() > this.expenses&& mNextChain != null) {
            this.mNextChain.handleRequest(request);
        }else{
            System.out.println("組長批準(zhǔn)了");
        }
    }
}

我們看到這里處理?xiàng)l件傳進(jìn)去200,說明我只能處理兩百塊的出差申請,然后如果費(fèi)用太高只能讓上面的人來審批,當(dāng)然這里判斷不充分,但是anyway,我們只要了解思想即可。接著我們看下一個(gè)處理者:

public class ApartmentDirector extends Handler{

    public ApartmentDirector() {
        super(400);
    }

    @Override
    public void handleRequest(Request request) {
        if (request.getApplyExpense() > this.expenses && mNextChain != null) {
            this.mNextChain.handleRequest(request);
        }else{
            System.out.println("主管批準(zhǔn)了");
        }
    }
}

我們看到這里的處理?xiàng)l件是400,接著我們看下一個(gè)處理者:

public class ApartmentManager extends Handler{

    public ApartmentManager() {
        super(500);
    }

    @Override
    public void handleRequest(Request request) {
        if (request.getApplyExpense() > this.expenses) {
            //this.mNextChain.handleRequest(request);
            System.out.println("不符合公司規(guī)定");
        }else{
            System.out.println("經(jīng)理批準(zhǔn)了");
        }
    }
}

到這里我們所有的處理者都已經(jīng)出現(xiàn)了,接著就是我們的請求對象了。我們同樣建一個(gè)請求者的抽象角色:

public abstract class Request {
    public int requestExpense;
    
    public Request(int expense){
        this.requestExpense = expense;
    }
    
    public int getApplyExpense(){
        return this.requestExpense;
    }
}

然后建一個(gè)具體的請求者角色:

public class BusinessRequest extends Request{

    public BusinessRequest() {
        super(500);
    }
}

到這里我們所有的角色都已經(jīng)完成了,我們看下我們這個(gè)責(zé)任鏈?zhǔn)窃趺词褂玫模?/p>

        Handler teamLeader = new TeamLeader();
        Handler apartmentDirector = new ApartmentDirector();
        Handler apartmentManager = new ApartmentManager();
        //設(shè)置鏈?zhǔn)疥P(guān)系
        teamLeader.setNextHandler(apartmentDirector);
        apartmentDirector.setNextHandler(apartmentManager);
        
        //請求事件
        Request request = new BusinessRequest();
        //處理請求
        teamLeader.handleRequest(request);

我們看到我們請求傳到這條鏈?zhǔn)秸{(diào)用關(guān)系上面去,然后根據(jù)申請的費(fèi)用額度每個(gè)經(jīng)理人會(huì)判斷是否處理。但是這里有個(gè)問題,我們不能得到下家的反饋,也許我們需要對下家反饋?zhàn)鲆粋€(gè)再處理呢?那么我們這里把這個(gè)變型一下。

1.改版后的責(zé)任鏈設(shè)計(jì)模式

我們首先來看抽象處理者。

public abstract class Handler {
    protected int expenses;
    protected Handler mNextChain;
    
    public Handler(int expenses){
        this.expenses = expenses;
    }
    
    public void setNextHandler(Handler handler){
        this.mNextChain = handler;
    }
    
    public abstract String handleRequest(Request request);
}

我們看到這里唯一的不同是我們這里handleRequest()方法增加了String返回值表示我們的處理返回值。然后我們先來看看組長角色:

public class TeamLeader extends Handler{
    
    public TeamLeader() {
        super(200);
    }

    @Override
    public String handleRequest(Request request) {
        
        if (request.getApplyExpense() > this.expenses&& mNextChain != null) {
            String response = this.mNextChain.handleRequest(request);
            if ("不符合公司規(guī)定".equalsIgnoreCase(response)) {
                return "先給你批準(zhǔn)"+this.expenses;
            }else{
                return response;
            }
        }else{
            return "組長批準(zhǔn)了";
        }
    }
}

我們看到了,這里針對下家處理返回的值還進(jìn)行了處理,也就是說我們每一個(gè)節(jié)點(diǎn)都可以針對這個(gè)進(jìn)行處理包裝和返回結(jié)果。達(dá)到了功能增強(qiáng)的作用。另外兩個(gè)節(jié)點(diǎn)也是類似,我就不具體貼出來代碼了,之所以講這個(gè)是因?yàn)槲覀僌kHttp中用的責(zé)任鏈設(shè)計(jì)模式就是我們改版的模型。我們直接來欣賞下吧。

2.OkHttp中的責(zé)任鏈設(shè)計(jì)模式

我們上次已經(jīng)說過了OkHttp的源碼了,如果有興趣可以看OkHttp源碼分析,這里我們先來貼一張?jiān)韴D:

OkHttp流程圖

我們知道非常關(guān)鍵的地方就是一系列的攔截器,每個(gè)攔截器負(fù)責(zé)不同的功能,然后針對下個(gè)攔截器的返回reponse進(jìn)行再處理。首先我們來看抽象處理者角色:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;
//省略一些跟講解無關(guān)代碼
......
  }
}

我們看到proceed方法就是我們這里傳遞處理的方法。然后程序會(huì)講實(shí)現(xiàn)了Interceptor接口的攔截器添加到攔截器集合里面:

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

然后在RealInterceptorChain的里面會(huì)調(diào)用proceed進(jìn)行攔截器里面方法的調(diào)用:

  // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

然后我們看到攔截器(也就是我們具體處理者角色),這里面的代碼,首先我們看第一個(gè)攔截器retryAndFollowUpInterceptor攔截器:

@Override public Response intercept(Chain chain) throws IOException {
.....
  try {
        response = realChain.proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
....
      } catch (IOException e) {
....
        } finally {
....
         }
.......
}

我們看到這里的proceed方法又交給了下個(gè)攔截器處理,那么我們來看下個(gè)攔截器BridgeInterceptor:

 @Override public Response intercept(Chain chain) throws IOException {
....
  Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
......
 return responseBuilder.build();
  }

我們看到這個(gè)攔截器也調(diào)用了proceed方法把請求提交給下個(gè)攔截器,以此類推,下面的攔截器都是有這個(gè)操作,直到最后一個(gè)攔截器把結(jié)果返回,然后才會(huì)一級一級回歸到第一個(gè)攔截器,然后對結(jié)果進(jìn)行再處理,這里的第一個(gè)攔截器就是執(zhí)行的重新請求等一些處理的。
總結(jié):所以我們看到責(zé)任鏈設(shè)計(jì)模式不僅可以針對鏈?zhǔn)街忻總€(gè)節(jié)點(diǎn)處理結(jié)果進(jìn)行增強(qiáng)(OkHttp中的攔截器),而且可以針對請求進(jìn)行截?cái)?事件分發(fā)機(jī)制),可想而知,這個(gè)設(shè)計(jì)模式使用還是非常廣泛的,希望這里說的有什么不足大家可以提出來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,564評論 19 139
  • 1 場景問題# 1.1 申請聚餐費(fèi)用## 來考慮這樣一個(gè)功能:申請聚餐費(fèi)用的管理。 很多公司都有這樣的福利,就是項(xiàng)...
    七寸知架構(gòu)閱讀 3,282評論 3 58
  • 主目錄見:Android高級進(jìn)階知識(shí)(這是總目錄索引)?OkHttp的知識(shí)點(diǎn)實(shí)在是不少,優(yōu)秀的思想也有很多,這里只...
    ZJ_Rocky閱讀 2,401評論 2 6
  • 被別人問到,自己只知道這個(gè)名字,但是完全不知道內(nèi)容,竟然還是這么常用的設(shè)計(jì)的模式,丟人啊,然后找到一篇,介紹簡單好...
    壹人城閱讀 1,235評論 0 7
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,093評論 1 15

友情鏈接更多精彩內(nèi)容