责任链模式及OkHttp中的实现

责任链模式及OkHttp中的实现

责任链模式

责任链模式是对一个事件的处理方法,所有能对事件进行处理的对象按顺序形成一个链表.事件经过链表中每个处理对象轮流处理.如果有返回值.则返回也是顺着这条链表反向返回.这个链表是先进后出模式.

  • 在现实中的责任链模型之一就是网络连接.对与程序猿而言,七层或五层的网络连接模型是肯定知道的.

当一个网络请求发出时,需要经过应用层->传输层->网络层->连接层->物理层

收到响应后正好反过来,物理层->连接层->网络层->传输层->应用层

在请求经过各层时,由每层轮流处理.每层都可以对请求或响应进行处理.并可以中断链接,以自身为终点返回响应

  • 另一个现实模型就是Web服务器的请求缓存

一个请示从客户端发送到Web服务器需要经过客户端->中间服务器->反向代理->Web端缓存(如Redis)->Web数据库

除了Web数据库是请求的点外,中间所有层都可以缓存数据.如果有缓存时会终止请求,以自身为终点返回数据.

如果途经的层没有缓存,则会在收到下一级的返回的数据时对数据进行缓存.然后返回上一层.

在设计模式中,负责链模式就是对这种顺序处理事件的行为的抽象,通过接口来定义处理事件的方法.顺序分发/处理事件.

  • 每个责任人实现相同的接口,处理一个事件对象
  • 让事件对象责任人之间顺序传递
  • 事件的处理结果的返回是逆序的
  • 责任链中的每个责任人都可以有权不继续传递事件,以自身为终点处理事件返回结果

OkHttp中的责任链模式

Okhttp中,Intercepter就是典型的责任链械的实现.它可以设置任意数量的Intercepter来对网络请求及其响应做任何中间处理.比如设置缓存,Https的证书验证,统一对请求加密/防串改,打印自定义Log,过滤请求等.

interface Intercepter{
Response response chain(Chain chain);
}

这个接口很简单,拿到Chain对象.最后返回个Response,那这个对象是什么鬼??

它有两个重要的方法
public Request getQuest()public Response process(Request quest)
拿到请求及设置请求拿到响应.
一般的实现是先拿到请求.然后对请求做一番蹂躏,然后process一下,拿到Response,折腾一下.然后return

Response response chain(Chain chain){
  Requset quest = chian.getRequset()
  ooxx(request);
  Response response = chian.process(quest);
  xxoo(response);
  return response;
}

或许它不知道,Resquse其实是被上家ooxx过的,Respnse也是被下家xxoo过的.

具体的实现可以去看一下源码,我这里就写一下自己理解的超简单的实现

public class Okhttp {
private List<Intercepter> mIntercepters=new LinkedList<>();
private Request mRequest;
private int mIndex;
private Callback mCallback;
private Chain mChain=new Chain();
private Intercepter mNetIntercepter =new Intercepter() {
@Override
public Response chain (Chain chain) {
  //这里是真实的发送网络请求
  return 网络请示的响应;
  }
};

/**
* 添加拦截器,后加的放最前面
* @param intercper 拦截器
*/
public void addIntercepter(Intercepter intercper){
  mIntercepters.add(0, intercper);
}

/**
* OkHttp请求的入口
* @param request 请示
* @param callback 回调
*/
public void execute(Request request ,Callback callback){
  mCallback=callback;
  mIndex=0;
  mRequest=request;
  new Thread(new Runnable() {
  @Override
  public void run () {
    Response response=mChain.process(mRequest);
    mCallback.onResponse(response);
    }
  }).start();
}

/**
* 定义的一个类用与递归的方式完成责任链发送请求获取响应
*/
private class Chain {
public Request getRequest(){
  return mRequest;
}

/**
* 顺序获取拦截器,传递chain对象给拦截器,获取响应
* @return 请求的响应
*/
private Response getResponse () {
  Intercepter intercepter = mIntercepters.get(mIndex);
  mIndex++;
  return intercepter.chain(this);
}

/**
* 如果责任链没走完,则顺序从责任链中获取拦截器,处理请求
* 否则由真正的负责网络请求的拦截器处理请求
* @param request 请求
* @return 响应
*/
Response process(Request request){
  mRequest=request;
  if (mIndex>=mIntercepters.size()){
    return Okhttp.this.mNetIntercepter.chain(this);
  }else{
    return getResponse();
  }
}
}
}

不得不说程序猿写文章真是容易骗字数.我已经极力在简化了.代码还是差不多上百行.

应该写得够简单吧.重点也就几个

  1. 内部有个处理真实网络请求的Intercepter,做为最后的接盘侠.
  2. 返回响应的是intercepter.chain(this),如果这个拦截器调用了chainprocess()方法就形成递归,否则就是终断了请求的传递
  3. 整个责任链的传递都是同步的.整体在子线程运行,最后通过回调返回响应.

还有一个很经典的实现就是Android的事件分发机制.这里我就不贴代码骗字数了.网上随便一找一堆.

责任链模式的用途

当业务逻辑需要形成一个事件处理流时,就可以考虑使用责任链模式.通过接口来规范中间环节的行为,专注与事件流的传递.可以随意扩展及调整中间环节.

我在实际业务中的有一个场景中使用了责任链模式.(当时使用的时候其实并不清楚)

  1. 在一个列表中可以弹出一个筛选菜单,菜单项是不定的.某些项选择后可以增加更多的选项条目
  2. 选项条目类型不同.有单选,多选,输入等.
  3. 点击完成时才把所有筛选条目形成对应的网络请求参数

实际处理起来很简易.定义了两个类

interface ParamsSetAble{
void setParams(NetParams params);
}

public class NetParams{}

每个条目实现ParamsSetAble接口用与设置参数.

NetParams类用与收集参数,最后转换成网络请求所用的格式

当点击完成时,遍历所有的条目,传递NetParams对象.最后把这个对象传递给网络请求的类.

在传递时进行可以进行参数检查,错误时可以中断传递,提示错误.我这里是通过手动抛异常来中断的.在外部统一catch处理.如果用Rxjava可以不用catch,在onError里统一管理.如Observer.error(new 自定义异常(中断提示信息))

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,504评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,123评论 25 707
  • 1 场景问题# 1.1 申请聚餐费用## 来考虑这样一个功能:申请聚餐费用的管理。 很多公司都有这样的福利,就是项...
    七寸知架构阅读 3,071评论 3 58
  • 0.作业要求 使用ASN.1编写一个数据结构。数据结构自己考虑。 分别使用asn1c、JavaAsn1Compil...
    htkz阅读 16,004评论 7 7
  • 如果看过我前面几篇关于Runtime的文章,应该知道Runtime的消息发送机制的原理是对象根据方法编号SEL去映...
    FITZ9311阅读 425评论 0 3