美文网首页
责任链模式

责任链模式

作者: YIHENGZI | 来源:发表于2019-10-12 16:34 被阅读0次
    责任链模式定义

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

    设计模式书解析与实战一书上的定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

    通俗点讲:顾名思义,责任链就是把处理者串成一条链,各个处理者为链上的各个节点,每个节点都有各自的责任及处理方式。当有事件请求时,第一个链节点的处理者看自己是否要处理,如果要的话就自己处理,如果不要的话就交给下一个节点的处理者去处理。以此类推,直到最后一个节点的处理者

    大致示意图
    image.png
    举例:

    1、建议
    雇员提了一个公司的建议给主管
    主管若认同这个建议,则继续上报给经理,否则直接批驳此建议
    经理收到建议,也同样认同觉得还不错,则继续上报给董事长,否则直接批驳此建议
    董事长也认同此建议,则执行,不认同则驳回建议

    2、请假
    学生去跟老师请假
    如果请假一天内,班导师可以直接处理,如果超过1天,则交由教务处辅导员来处理
    如果请假三天内,辅导员可以处理,超过3天,则要由院长来处理
    如果院长同意,则审批通过,如果不同意,则打回请假请求

    责任链模式的显著特点

    1、请求由起始点,顺着链路,依次向下传递,直至传递到最后一个处理者,所有的处理者都可以共享到这个请求体
    2、处理者并不关心上一下或下一个处理者,只需要将当前处理进度组织成处理者遵循的接口规范即可,且确保处理者的响应也遵循统一的接口规范

    责任链设计思想

    1、由于一个请求会被多个处理者进行处理,这就需要请求的处理者在接收请求时,需要遵循指定的接口规范。即需要对处理者进行统一接口规则定义
    2、由于责任链模式中,当前处理者对请求进行处理后,会将响应返回。因此,这就需要所有的处理者返回响应头,也要遵循统一的接口规范。
    3、请求处理者可能会存在多个,具体处理者并不能确定,而且处理者的执行顺序不同,常发生在流程改变,或某个流程规则发生替换时,因此这就需要有一个容器,将所有的请求处理者进行保存,并且这个窗口需要由客户端传递过来。此外还要确保容器的数据类型,定义为处理者接口,只有这样,容器中才可以添加不同类型的处理者
    4、最后还要确保实现链式调用,使窗口中记录的请求处理者,根据请求,依次通过不同的请求处理者来处理请求。所以,接下来还需要提供一个专门用于执行请求处理者的执行链。

    执行链需要记录客户端指定的所有请求处理者,并记录当前请求处理者的光标索引,同时还需要记录客户端请求。这样一来,当执行链被触发时,会在触发调用的方法中,再次创建一个执行链next,并为这个next定义光标值+1,这就会使next这个执行链被执行时,从处理者容器中获取下一个处理者并将其执行,而这下一个处理者,会再次创建一个执行链,并在此基础上更新光标数值。这样一来,执行链会被多次创建,而每次创建执行时,都会访问获取处理者容器中的下一个元素,并将该元素执行,这样就将容器中的所有处理者,以链的方式一一执行。每个处理者元素,都将会返回响应,由于这时已经确保所有处理者按照在容器中的顺序一一执行,因此,按照顺序将响应进行拼接,这就形成了客户端请求按照顺序,逐一被请求处理者执行,并最终将响应结果拼接,从而就实现了通过责任链模式的链式调用。

    应用场景

    1、多个对象可以处理同一请求,但具体由哪个对象处理或是由多个对象共同处理则在运行时动态决定
    2、请求处理环节会发生改变,导致其中的环节也有可能发生改变,也有可能所有的环节都会改变更新。
    3、需要动态指定一组对象处理请求

    责任链类型

    1、纯的责任链模式:一个请求必须被某一个处理者接收,而且一个处理者对某个请求的处理只以采取自己处理,或推给下一个处理者来处理。
    2、不纯的责任链模式:允许出现某一个处理者在接收承担了一部分责任(即已经做了一些处理)后,再把请求传给下一个处理者去进行处理,且这个请求最终可以不被任意处理者接收

    优缺点

    优点:
    1、解耦了请求与处理
    2、处理者只需要关注自己职责需要处理的请求即可,对于不是职责需要的请求,直接转给下一节点处理对象
    3、链式传递处理请求,请求者无需关注链路处理者结构,只需要等待处理结果就好
    4、链式结构灵活,可以通过链路结构动态地新增或删减处理者
    5、易于扩展新的处理对象,符合开闭原则(即对于扩展是开放的,但是对于修改是封闭的)

    缺点:
    1、链路过长的时候,会影响请求传递的处理效率,损耗性能
    2、如果节点对象存在循环引用时,会造成死循环,导致崩溃
    3、请求不一定会被处理

    代码实现

    简陋地实现学生请假责任链流程
    1、定义拦截器抽象接口

    interface IHandler {
    
        /**
         * 拦截操作 每个拦截器将会在此方法中根据拦截链Chain来触发下一个拦截器的调用,
         * 直至最后一个才不进行触发
         *
         * @return 每个拦截器处理的结果
         */
        fun intercept(chain: Chain) : String
    
        /**
         * 拦截链抽象接口
         */
        interface Chain {
    
            /**
             * 请求
             */
            fun request() : Request
    
            /**
             * 处理请求
             */
            fun process() : String
        }
    }
    

    2、实现拦截链

    open class RealChain(
        private val handlers: ArrayList<IHandler>,
        private val index: Int,
        private val request: Request
    ) : IHandler.Chain{
    
        override fun request(): Request {
            return request
        }
    
        /**
         * 在process方法中,每次调用该方法时都会创建一个RealChain对象,并根据当前index光标索引,获取当前拦截器,执行intercept方法,并传入下一个next
         * 这样一来就可以在具体的IHandler处理拦截器中,来决定是否需要继续调用RealChain对象的process方法,如果继续调用此方法,则框架会继续执行下一个节点的拦截操作
         * 否则,将会终止,并返回结果
         */
        override fun process() : String {
            if (index > handlers.size - 1) {
                return "无人审批!"
            }
    
            // 生成下一个链节点
            val next = RealChain(handlers, index + 1, request)
            // 执行当前的请求任务,并传入到下一个处理者中
            // 从面使拦截链可以执行interceptors容器中的下一个拦截器
            val handler = handlers[index]
            return handler.intercept(next)
        }
    }
    

    3、实现具体的拦截处理器

    class TeacherHandler : IHandler {
        override fun intercept(chain: IHandler.Chain) : String {
            val request = chain.request()
            return if (request.day <= 1) {
                "班导觉得 ${request.reason} 理由充分,可以批准请假"
            } else {
                "班导处理不了,交给下一级处理\n${chain.process()}"
            }
        }
    }
    

    班导处理器模拟了班导师审核请假时候的操作,自己不能处理时,通过RealChain使下一个处理器执行

    class CounselorHandler : IHandler {
        override fun intercept(chain: IHandler.Chain) : String {
            val request = chain.request()
            return if (request.day in 2..3) {
                "辅导员觉得 ${request.reason} 理由充分,批准了请假"
            } else {
                "辅导员处理不了,交给下一级处理\n${chain.process()}"
            }
        }
    }
    

    辅导员处理器判断自己是否处理请求,或不处理,继续通过RealChain来使下一个处理器执行

    class PresidentHandler : IHandler {
        override fun intercept(chain: IHandler.Chain) : String {
            return if (chain.request().day < 11) {
                "院长觉得 ${chain.request().reason} 理由充分,批准了请假"
            } else {
                "院长觉得请假${chain.request().day}天时间太长,不批"
            }
        }
    }
    

    院长处理器是最后一个处理器了,所以便不用再通过RealChain向下一个处理器派发请求了

    4、开始申请,责任链调用

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
    
            val handlers = arrayListOf<IHandler>()
            handlers.add(TeacherHandler())
            handlers.add(CounselorHandler())
            handlers.add(PresidentHandler())
            val chain = RealChain(handlers, 0, Request(day = 3, reason = "不想上课"))
            println("请假审批开始")
            println(chain.process())
        }
    }
    

    5、结果

      I/System.out: 请假审批开始
      I/System.out: 班导处理不了,交给下一级处理
      I/System.out: 辅导员处理不了,交给下一级处理
      I/System.out: 院长觉得 不想上课 理由充分,批准了请假
    
    责任链模式在okhttp上使用 - 拦截器

    okhttp中的拦截器,一个网络请求,按一定的顺序,经由多个拦截器进行处理,该拦截器可以决定自己处理并且返回处理结果,也可以选择向下继续传递,让后面的拦截器处理返回它的结果。

    首先我们来看下同步情况下的流程,请求会走到RealCall execute()方法里边

    override fun execute(): Response {
        // 同一个call只能被执行一次
        synchronized(this) {
          check(!executed) { "Already Executed" }
          executed = true
        }
        transmitter.timeoutEnter()
        transmitter.callStart()
        try {
          // 将call添加到调度器里边
          client.dispatcher.executed(this)
          // 核心方法,责任链获取到Response结果
          return getResponseWithInterceptorChain()
        } finally {
          client.dispatcher.finished(this)
        }
      }
    
    @Throws(IOException::class)
      fun getResponseWithInterceptorChain(): Response {
        // 添加处理拦截器
        val interceptors = mutableListOf<Interceptor>()
        interceptors += client.interceptors
        interceptors += RetryAndFollowUpInterceptor(client)
        interceptors += BridgeInterceptor(client.cookieJar)
        interceptors += CacheInterceptor(client.cache)
        interceptors += ConnectInterceptor
        if (!forWebSocket) {
          interceptors += client.networkInterceptors
        }
        interceptors += CallServerInterceptor(forWebSocket)
    
        // 责任链 创建拦截链实例 将上面的拦截器添加到责任链里面
        val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
            client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
    
        ...
      
         val response = chain.proceed(originalRequest)
    
        ...
    }
    

    可以看到,getResponseWithInterceptorChain()方法,是将自定义的拦截器以及默认的几个拦截器添加到拦截链中去,然后执行链中的处理方法proceed,一直往下调
    再看看proceed()方法

    @Throws(IOException::class)
      fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
        ...
        
        // 责任链 把index + 1,创建下一个RealInterCeptorChain节点对象 
        val next = RealInterceptorChain(interceptors, transmitter, exchange,
            index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
        val interceptor = interceptors[index]
    
        @Suppress("USELESS_ELVIS")
        // 执行默认的拦截器
        val response = interceptor.intercept(next) ?: throw NullPointerException(
            "interceptor $interceptor returned null")
    
        ...
    
        return response
      }
    

    从这也可以看出chain.proceed()方法是如何进行链式传递的。类似递归,每次进行RealInterceptorChain(index + 1),使当前拦截器拥有下一个拦截链节点的引用,那当前拦截器就可以通过这个引用调用下一个chain.proceed()方法来传递请求了
    可以看一下默认的RetryAndFollowUpInterceptor拦截器

    class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
    
      @Throws(IOException::class)
      override fun intercept(chain: Interceptor.Chain): Response {
      ...
    
      //调用proceed(),传递请求到下一个拦截器
      response = realChain.proceed(request, transmitter, null)
    
      ...
    }
    

    如果还不是很清楚,可以看一下下面这张图,图示相对直观多了


    image.png
    参考

    https://blog.csdn.net/qq_15274383/article/details/78485648
    https://juejin.im/post/5d9cb062e51d45780c34a83e
    https://blog.csdn.net/sunyao19940708/article/details/81539767

    相关文章

      网友评论

          本文标题:责任链模式

          本文链接:https://www.haomeiwen.com/subject/kiswuctx.html