本文对应设计模式与范式:行为型(62-63),责任链模式 (Chain of Responsibility Pattern),常用于框架开发中,为框架提供扩展点,让框架使用者在不修改框架源码的情况下,基于扩展点添加新的功能,具体点说,最常用来开发框架的 拦截器 和 过滤器。
Tips:二手知识加工难免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。
原始定义
将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,知道链上某个接收对象能够处理它为止。
定义看起来有点抽象,简单点就是:构建一个处理流水线来对一次请求进行多次处理。
还是不懂?没关系,写个简单例子帮助理解~
你问哥哥、粑粑、麻麻拿钱,门槛依次是100,500,1000,只能按照一层层往下走,比如:
100块以下的开销,你可以找哥哥解决,100以上500以下的你得找爸爸,500以上1000以下你得找麻麻~
直接if-else一把梭,不难写出这样的代码:
public class ChainTest {
public static void main(String[] args) {
Random random = new Random();
int needMoney = random.nextInt(1500);
System.out.println("需要:" + needMoney + "块~");
if(needMoney < 100) {
System.out.println("哥哥:小于100块哥哥还是有的,给你~");
} else {
System.out.println("哥哥:大于100块哥哥木有那么多钱,找粑粑去吧~");
if(needMoney < 500) {
System.out.println("粑粑:500块以内,粑粑有,给你~");
} else {
System.out.println("粑粑:大于500,粑粑木有,找妈妈去吧~");
if(needMoney < 1000) {
System.out.println("麻麻:1000以内,麻麻可以报销,给你~");
} else {
System.out.println("麻麻:你要那么多钱干嘛?");
}
}
}
}
}
代码运行输出结果如下:
如果再来个疼爱孙子的爷爷,超过1000的可以找他报销,又得嵌套一个if-else,在一些复杂的实际业务场景,这样的写法可能要套十几层,对于这种流水线式加工的场景,其实可以使用责任链模式解耦~
把伸手要钱这个行为看成一个 请求,你是 请求者,家人是 接收者,他们会按照特定的顺序:哥哥 → 粑粑 → 麻麻 → 爷爷 对你的请求进行处理,在自己额度范围内,就不往下走,不在就往下走,直到最后一个接收者为止。这个特定顺序可以看成一条 链,请求就是沿着这样的链往后传递~
一种最简单的实现方式,就是:每个接收者持有后继接收者实例,递归调用直到没有后继接收者为止,实现代码如下:
// 抽象处理者
public abstract class AbstractHandler {
// 下一个处理者
private AbstractHandler nextHandler;
public AbstractHandler getNextHandler() { return nextHandler; }
public void setNextHandler(AbstractHandler nextHandler) { this.nextHandler = nextHandler; }
// 请求处理
public abstract void handleRequest(String msg, int money);
}
// 具体处理者
public class BrotherHandler extends AbstractHandler {
@Override
public void handleRequest(String msg, int money) {
System.out.println(msg + ":" + money + "块~");
if(money < 100) {
System.out.println("哥哥:小于100块哥哥还是有的,给你~");
} else {
System.out.println("哥哥:大于100块哥哥木有那么多钱,找粑粑去吧~");
if(getNextHandler() != null) getNextHandler().handleRequest(msg, money);
}
}
}
public class FatherHandler extends AbstractHandler {
@Override
public void handleRequest(String msg, int money) {
if(money < 500) {
System.out.println("粑粑:500块以内,粑粑有,给你~");
} else {
System.out.println("粑粑:大于500,粑粑木有,找妈妈去吧~");
if(getNextHandler() != null) getNextHandler().handleRequest(msg, money);
}
}
}
public class MotherHandler extends AbstractHandler {
@Override
public void handleRequest(String msg, int money) {
if(money < 1000) {
System.out.println("麻麻:1000以内,麻麻可以报销,给你~");
} else {
System.out.println("麻麻:你要那么多钱干嘛?");
if(getNextHandler() != null) getNextHandler().handleRequest(msg, money);
}
}
}
// 测试用例
public class ChainTest {
public static void main(String[] args) {
Random random = new Random();
int needMoney = random.nextInt(1500);
BrotherHandler brotherHandler = new BrotherHandler();
FatherHandler fatherHandler = new FatherHandler();
MotherHandler motherHandler = new MotherHandler();
// 指定下一个接收者
brotherHandler.setNextHandler(fatherHandler);
fatherHandler.setNextHandler(motherHandler);
// 开始请求传递
brotherHandler.handleRequest("狗子要钱", needMoney);
}
}
代码运行结果输出如下:
责任链模式解耦后,此时我们要再添加一个爷爷就很简单了,四步:
继承AbstractHandler → 重写handleRequest() → 初始化爷爷实例 → motherHandler.setNextHandler()
如果要再来个曾祖父或高曾祖父,也是这样操作,你可能会吐槽是不是有点过度设计,简单的if-else嵌套就能解决,这样会写多很多类,有点复杂化了。
应用设计模式主要是为了:应付代码复杂性,让其满足开闭原则,提高代码扩展性。而将大块代码逻辑拆解成函数、大类拆分成小类,是应对代码复杂性的常用方法。
此处使用责任链模式,把处理请求的函数拆分出来,设计成独立的类,简化了ChainTest类,使其不至于代码过多,过复杂。
客户端代码新增一个接收者,不需要去修改框架的代码,只需基于框架提供的扩展点进行扩展,可以说是:在框架代码范围内实现了开闭原则。
老规矩,带出UML类图、组成角色、使用场景及优缺点的总结
使用场景
优点
缺点
另外,除了使用上述 递归的方式形成链条 实现责任链模式外,还可以使用数据结构(数组、列表、链表等) 按顺序保存具体处理者实例,然后遍历的方式实现~
Android著名的第三方请求库OkHttp中的拦截器,就用到了责任链模式,我们来扒一扒具体的实现原理~
通过继承 Interceptor
接口,重写intercept(Chain)函数来实现一个自定义拦截器,看下接口:
接着通过 addInterceptor()
函数添加自定义拦截器,跟下:
看下 interceptors
:
呕吼,拦截器列表,把自定义拦截器加到其中,看下哪里用到这个列表了:
这里有两个知识点:
不能修改的列表,底层原理
继承List,对修改元素的函数进行重写,抛出UnsupportedOperationException异常
可以写个简单的代码验证下:
运行后报错,异常如下:
但真的就不可变吗,如果换成修改原列表呢?
运行打印结果如下:
操作原列表,导致不可变的列表发生了改变,看源码很好理解:
代理模式,即控制对目标对象的访问,UnmodifiableList只是对目标对象裹了一层,不经过它通过其他方式修改了目标对象,那肯定会发生改变啊。所以OkHttp不是直接调,而是:
基于原始列表,创建了一个 新的列表,杜绝了外部对目标对象的改动,真是妙啊!
继续跟 networkInterceptors
,定义了一个公共获取拦截器列表的方法:
看下哪里用到了,定位到 RealCall.getResponseWithInterceptorChain()
中:
把拦截器全加到列表里,等下按顺序走,很好理解,然后是这个 RealInterceptorChain
,关注下传入参数,除了关注interceptors列表外,还要关注一个 index,传入的值为0。
然后下面调用了 chain.proceed(originalRequest)
返回了一个Response对象,点进去类看看:
核心代码是圈住那里,拆解下步骤:
这里其实就是 递归
,先回到一开始的接口:
责任链模式有两个要点:如何往后传递处理结果 和 何时结束传递
第一点传递很好理解:
Interceptor实现类调用 intercept(Chain) 往下传递Chain实例,实例中包含了拦截器列表、下一个拦截器的下标、处理后的request实例 等。
第二点何时结束:
最后一个拦截器调用
chain.proceed()
返回Response实例为止,而这个实例会往前传递,前面调用了chain.proceed()
的拦截器可以获得Response实例并进行加工(如打日志),第一个调用了chain.proceed()
的拦截器得到的就是处理完后的Response。
所以OkHttp的拦截器/过滤器是 双向
的,而Chain接口就是链接其中的链条:
- 前 → 后:request()获得当前request实例,拦截器可对请求进行加工,可也调用intercept往下传递Chain;
- 后 → 前:proceed()获得当前response实例,拦截器可对响应进行加工,作为返回值往上传;
转载:https://juejin.cn/post/6993225793383956488
作者:coder_pig