流行框架源码分析(13)-责任链设计模式

主目录见:Android高级进阶知识(这是总目录索引)
 学习设计模式,个人觉得第一就是不能局限于一个思维,也许些许的变化有时能让它更加符合你的设计,所以我们不要拘泥于固定的角色,生搬硬套的场景,我们要经得起变化。这也是为什么会有那么多的变形的原因。我们今天要讲责任链设计模式(chain of responsibility),这个模式也是会有很多的变形,我们待会会在分析例子的过程中讲到。我们先来看下它的定义:

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

这个定义非常明确地说明了责任链模式的作用,很多对象由每一个对象对其下家的引用而连接起来形成一条链也就是说请求向上传递过程中,如果自己不处理的话,那么由于自己持有了下家的引用,所以可以交给下家去处理,这样循环下去直到有对象想要处理。我们接着来看看UML类图:


责任链设计模式

角色介绍:

  • Handler:抽象处理者,声明一个请求的处理方法,并保持对下一个处理节点Handler对象的引用。

  • ConcreteHandler:具体处理者,对请求进行处理,如果不处理就讲请求转发给下一个节点上的处理对象。

一.目标

讲这个设计模式,是觉得他用的地方还是蛮广的,而且我们最熟悉的事件分发应该就是典型的责任链设计模式吧,所以我们今天目标也是:
1.明白责任链设计模式怎么使用;
2.知道在什么场景需要用到责任链设计模式。

二.模式分析

同样地,我们今天也先假设一个场景,公司有一天安排你去出差,那你说我要申请费用500块,然后你申请表就提交给组长,组长说数额太大不归我管,让主管来吧,主管也说这个我也不能管,让经理来吧,经理一甩手,允许了,然后你就拿着钱屁颠屁颠出差去了。这里我们首先看看我们抽象处理者角色:

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);
}

我们看到这是一个典型的抽象处理者角色,里面有设置下家的引用setNextHandler(),还有处理条件expenses,处理方法handleRequest()。然后我们分别来看几个具体的处理者角色首先是组长:

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("组长批准了");
        }
    }
}

我们看到这里处理条件传进去200,说明我只能处理两百块的出差申请,然后如果费用太高只能让上面的人来审批,当然这里判断不充分,但是anyway,我们只要了解思想即可。接着我们看下一个处理者:

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("主管批准了");
        }
    }
}

我们看到这里的处理条件是400,接着我们看下一个处理者:

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("不符合公司规定");
        }else{
            System.out.println("经理批准了");
        }
    }
}

到这里我们所有的处理者都已经出现了,接着就是我们的请求对象了。我们同样建一个请求者的抽象角色:

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

然后建一个具体的请求者角色:

public class BusinessRequest extends Request{

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

到这里我们所有的角色都已经完成了,我们看下我们这个责任链是怎么使用的:

        Handler teamLeader = new TeamLeader();
        Handler apartmentDirector = new ApartmentDirector();
        Handler apartmentManager = new ApartmentManager();
        //设置链式关系
        teamLeader.setNextHandler(apartmentDirector);
        apartmentDirector.setNextHandler(apartmentManager);
        
        //请求事件
        Request request = new BusinessRequest();
        //处理请求
        teamLeader.handleRequest(request);

我们看到我们请求传到这条链式调用关系上面去,然后根据申请的费用额度每个经理人会判断是否处理。但是这里有个问题,我们不能得到下家的反馈,也许我们需要对下家反馈做一个再处理呢?那么我们这里把这个变型一下。

1.改版后的责任链设计模式

我们首先来看抽象处理者。

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 ("不符合公司规定".equalsIgnoreCase(response)) {
                return "先给你批准"+this.expenses;
            }else{
                return response;
            }
        }else{
            return "组长批准了";
        }
    }
}

我们看到了,这里针对下家处理返回的值还进行了处理,也就是说我们每一个节点都可以针对这个进行处理包装和返回结果。达到了功能增强的作用。另外两个节点也是类似,我就不具体贴出来代码了,之所以讲这个是因为我们OkHttp中用的责任链设计模式就是我们改版的模型。我们直接来欣赏下吧。

2.OkHttp中的责任链设计模式

我们上次已经说过了OkHttp的源码了,如果有兴趣可以看OkHttp源码分析,这里我们先来贴一张原理图:

OkHttp流程图

我们知道非常关键的地方就是一系列的拦截器,每个拦截器负责不同的功能,然后针对下个拦截器的返回reponse进行再处理。首先我们来看抽象处理者角色:

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

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;
//省略一些跟讲解无关代码
......
  }
}

我们看到proceed方法就是我们这里传递处理的方法。然后程序会讲实现了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的里面会调用proceed进行拦截器里面方法的调用:

  // 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);

然后我们看到拦截器(也就是我们具体处理者角色),这里面的代码,首先我们看第一个拦截器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方法又交给了下个拦截器处理,那么我们来看下个拦截器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();
  }

我们看到这个拦截器也调用了proceed方法把请求提交给下个拦截器,以此类推,下面的拦截器都是有这个操作,直到最后一个拦截器把结果返回,然后才会一级一级回归到第一个拦截器,然后对结果进行再处理,这里的第一个拦截器就是执行的重新请求等一些处理的。
总结:所以我们看到责任链设计模式不仅可以针对链式中每个节点处理结果进行增强(OkHttp中的拦截器),而且可以针对请求进行截断(事件分发机制),可想而知,这个设计模式使用还是非常广泛的,希望这里说的有什么不足大家可以提出来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容