一、能力越大责任越大
顾名思义,“责任链”就像是一根链条串联起来的一系列操作,每个操作都息息相关。请假的审批流程,报销流程等等依据不同职位对应的人员完成相应的审批操作;可以说是层层推进的。而我们最终关心的结果就是同意或者驳回。
二、责任链模式 Chain of Responsibility
当请求端Client发出请求时,为了降低请求者
Client
与处理对象们Handlers
之间的耦合;同时满足众多Handlers
都能有机会参与到对请求的处理,将Handler
对象组成一条链。而请求随着链不断传递,直到被处理并返回结果。
三、组成部分
通过定义可以发现,责任链主要有几个组成部分:
- 客户端请求类
Client
:发起请求,相当于开启责任链,提交任务到责任链等待处理结果。 - 处理类的抽象接口
Handler
或者抽象类:包含处理请求方法的定义,通常也包含后继链指向。 - 具体的
Handler
的实现类ConcreteHandler
:根据自身能力的大小对Client
的请求的具体实现,有可能刚好匹配那么直接处理返回结果链的调用结束,否则将请求转发到后继链继续处理直到请求处理完成。 - 结构图如下:
四、简单的代码实现
1.设计一个文件的解析系统
文件解析的系统根据导入的文件格式,匹配不同的解析对象
Handler
对其进行解析,通过链的开启,有可能第一次就匹配到合适的类那么链的调用结束并返回结果;当然如果是不支持的的文件类型-那么作为程序设计时是不是就可以简单的转化为当前对象无法处理这个请求,并且没有后续Handler
,通俗的讲即此时请求到链尾且仍没有合适的Handler
能够处理,则调用结束给出相应的报错信息或者其他操作反馈到请求者Client
。
- 抽象的处理类
Handler
:
/**
* Created by Sai
* on: 12/01/2022 23:53.
* Description:
*/
public abstract class FileParser {
//后继链
private FileParser next;
public void setNext(FileParser next) {
this.next = next;
}
//文件的扩展信息
protected abstract String getExtension();
protected abstract void doParse(String fileName);
public void read(String fileName) {
if (fileName.endsWith(getExtension())) {
this.doParse(fileName);
return;
}
if (next != null) next.read(fileName);
else System.out.println("the file type " + fileName + " is unsupported ");
}
}
具体的文件解析实现类、如Excel
、MarkDown
、Word
三个组成一条责任链。
Excle
文件解析类
/**
* Created by Sai
* on: 12/01/2022 00:01.
* Description:
*/
public class ExcelParser extends FileParser {
@Override
protected String getExtension() {
return ".xls";
}
@Override
protected void doParse(String fileName) {
System.out.println("Parse the excel file...... ");
System.out.println("-------------------------------------->");
}
}
MarkDown
文件解析具体实现类
/**
* Created by Sai
* on: 12/01/2022 00:03.
* Description:
*/
public class MarkDownParser extends FileParser {
@Override
protected String getExtension() {
return ".md";
}
@Override
protected void doParse(String fileName) {
System.out.println("Parse the markdown file......");
System.out.println("---------------------------------------->");
}
}
Word
文件解析具体实现类
/**
* Created by Sai
* on: 12/01/2022 00:10.
* Description:
*/
public class WordParser extends FileParser {
@Override
protected String getExtension() {
return ".doc";
}
@Override
protected void doParse(String fileName) {
System.out.println("Parse the word file......");
System.out.println("----------------------------------->");
}
}
当然如果确定Client
的请求比较明确,像此“文件”解析的系统,只需要导入文件,那么对于链的开启可以单独抽取出来。
- 实现解析工具类
/**
* Created by Sai
* on: 12/01/2022 00:13.
* Description:
*/
public class FileParserFactory {
public static FileParser getDataReaderChain() {
var excelParser = new ExcelParser();
var markDownParser = new MarkDownParser();
var wordParser = new WordParser();
wordParser.setNext(markDownParser);
markDownParser.setNext(excelParser);
return wordParser;
}
}
Client
测试类
/**
* Created by Sai
* on: 12/01/2022 00:15.
* Description:
*/
public class Demo {
public static void show() {
var reader = FileParserFactory.getDataReaderChain();
reader.read("file.xls");
reader.read("file.md");
reader.read("file.doc");
reader.read("file.jpg");
}
public static void main(String[] args) {
show();
}
}
- 打印信息,显然对于
JPG
格式系统是不支持,那么也给到了Client
相应的反馈信息
Parse the excel file......
success......
-------------------------------------->
Parse the markdown file......
success......
---------------------------------------->
Parse the word file......
success......
----------------------------------->
the file type file.jpg is unsupported, failed
Process finished with exit code 0
2.简单的总结
Client
作为请求发起者,如导入文件并解析出想要得到的结果。与处理类之间耦合程度低,Client
端只管文件的导入,并不会关心最终由谁来处理。即使系统以后需要扩展新的解析类也是非常方便的,只需要继承Handler
并单独实现具体细节即可,比较易于扩展的。但是同样也会伴随着系统的膨胀,跟职责
的粒度有关。另外观察也可发现,如果责任链太过于长的话,调用栈势必会很深。系统的性能也会打折扣,当然这也是根据具体的业务具体来考虑的。该用的时候想好怎么用,不该用的时候没必要为了设计而设计。毕竟设计模式本质仅仅是为了降低复杂度,降低耦合提高扩展性为目标。
Client
请求端与处理端Handler耦合度低
。- 职责的分配可以根据业务需求灵活组合,而修改某一具体职责实现细节不影响整体系统的稳定。
- 易于扩展。
当然缺点也是存在的
- 当职责的增加,链的长度增加,调用栈深度加深则会影响系统的效率。
- 职责的具体实现类如果较多,增加了一定的维护成本,同时
Client
端开启链时复杂度提高。
五、okHttp中的责任链模式
早期Java
版本,okHttp3
最新的版本为4.9.3
,不知道从哪个版本起已经改为Kotlin
实现,实现细节作出了调整,但对于拦截器
责任链的主要流程变动很小。
OkHttpClient
相当于我们网络的配置控制中心,包括一些基础的配置,连接池、重试、桥接、协议等,主要配置:Dispatcher(线程调度)
设定了最大请求数,单个Host
的最大请求数。Protocols
支持的协议,HTTP/1.1、HTTP/2。ConnectionSpec
对于Socket的设置信息,明确是明文传输的HTTP,还是TLS的HTTPS。Interceptor
,核心链,包含了几个重要的拦截器。当然还包括其他的一些基础参数。
1.newCall
从实际运用的例子入手:
private static void connectNet() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
//异步请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("Failed----->" + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("Success----->" + response.toString());
}
});
}
newCall(request)
方法返回的是一个RealCall
对象,实现了Call
的接口,当调用RealCall.execute()
时:
RealCall.getResponseWithInterceptorChain()会被调用,发起网络请求并拿到返回的响应值R