责任链模式应用场景非常多、比如拦截器、过滤器等等。但是要彻底理解责任链的实现原理还是有一定难度的,因此,责任链模式的实现原理也就成为了一道互联网大厂的高频面试题。今天,我给小伙伴们来详细地掰一掰,保证让你彻底搞明白,不服来战。
关于责任链模式的定义,官方原文是这样描述的:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
翻译过来就是:
将链中每一个节点都看作一个对象,每个节点处理的请求均不同,且内部自动维护下一个节点对象。当一个请求从 的首端发出时,会沿着责任链预设的路径依次传递到每一个节点对象,直至被链中的某个对象处理为止。
简答一句话总结就是:将处理不同逻辑的对象连接成一个链表结构,每个对象都保存下一个节点的引用。
我们在平时处理工作中的时候,往往需要各部门协同合作来完成某一个任务。因为,每个部门都有各自的职责,所以,很多时候事情完成一半,就要会转交到下一个部门,甚至还要盖章,直到所有部门都审批通过,事情才能完成。这就相当于将皮球层层往上踢。
还记得小时候追过的一个电视剧,80后应该都看过,有位太极高手闯七层宝塔,每一层都住了一位武林高手,层层往上“过五关,斩六将”,打赢才能往上一层,打输了就踢回去,最后登顶.当时看得是热血沸腾,这也是责任链的一种场景。不过忘记电视剧叫什么名字了,小伙伴们可以评论区告诉我一下。
所以责任链模式又被戏称为“踢皮球”模式。那责任链模式的实现原理又是怎样的呢?
责任链又分为单向责任链和双向责任链,单向责任链比较简单也容易理解,双向责任链相当于是一个执行闭环,较为复杂。
我们先来分析单向责任链。它的结构是这样设计的,首先,设计一个单向链表的上下文Context,保存链表的头(head )引用 和 尾(tail)引用,Context的代码结构是这样的。
然后,在上下文中加入Handler,也就是处理业务逻辑的节点类,每个Hnandler都保存了下一个执行节点的引用,形成一条完整的执行链路。Handler的通用代码结构是这样的。
J2EE中的Filter过滤器、Spring中的Interceptor拦截器都是采用这样的单向链表的设计。
那双向链表又如何设计呢?它和单向链表的基本结构一致,我们来看,它只是在Handler中增加了对上一个节点的引用。这样责任链就行了一个执行闭环,就好比是环线地铁。来看它的通用代码结构是这样的。
Netty中的Piepline管道就是采用这样的双向链表的设计。
责任链模式一般还会结合建造者模式来使用,实现链式编程。
那这样设计又有什么优点和缺点呢?先给大家总结一下它的优点:
(1)实现了将请求与处理完全解耦。
(2)请求处理者只需关注自己职责范围内的请求进行处理即可,对于不是职责范围内的请求,直接转发给下一个节点对象。
(3)具备链式传递处理请求的功能,请求发送者不需要知晓链路结构,只需等待请求处理结果即可。
(4)链路结构灵活,可以通过改变链路结构动态地新增或删减责任。
那这样设计,会有哪些缺点呢?我也给大家总结一下:
(1)如果责任链路太长或者处理时间过长,会影响程序执行的整体性能。
(2)如果节点对象存在循环引用,有可能会造成死循环,从而导致程序崩溃。
小伙伴们,你还觉得责任链模式的实现原理难吗?