当前位置: 首页 > article >正文

重修设计模式-行为型-责任链模式

重修设计模式-行为型-责任链模式

将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止。

责任链模式(Chain of Responsibility Pattern)也叫职责链模式,通过将请求的处理分配给一系列的处理对象,这些处理对象通过链式结构组织起来,以实现对请求的灵活处理。比如,一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条,链条上的每个处理器承担各自的处理职责。

责任链模式的原理非常简单,有两种常用的实现。一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

1.链表方式

使用链表结构存储处理器,如果有处理器能处理该请求,就不继续往下传递;如果不能处理,则交由后面的处理器来处理。

//处理器
abstract class Handler {
    var next: Handler? = null

    abstract fun handle()
}

class Handler1: Handler() {
    override fun handle() {
        var handled = false
        //doAction...
        handled = Random.nextBoolean()
        println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")
        //...
        if (!handled && next != null) {  //如果处理不了,交给下一个处理器
            next?.handle()
        }
    }
}

class Handler2: Handler() {
    override fun handle() {
        var handled = false
        //doAction...
        handled = Random.nextBoolean()
        println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")
        //...
        if (!handled && next != null) {
            next?.handle()
        }
    }
}


//处理器链
class HandlerChain {
    var head: Handler? = null
    var tail: Handler? = null

    fun addHandle(handler: Handler) {
        handler.next = null
        if (head == null) {
            head = handler
            tail = handler
            return
        }
        tail?.next = handler
        tail = handler
    }

    fun handle() {
        head?.handle()
    }
}

//使用时:
fun main() {
    val chain = HandlerChain()
    chain.addHandle(Handler1())
    chain.addHandle(Handler2())
    chain.handle()  //开始责任链调用
}

2.集合方式

使用集合存储所有处理器,调用时通过遍历来串联所有处理器,实现更加简单

//处理器
abstract class HandlerList {
    abstract fun handle(): Boolean
}

class HandlerA : HandlerList() {
    override fun handle(): Boolean {
        var handled = false
        //doAction...
        handled = Random.nextBoolean()
        println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")
        //...
        //如果处理不了,交给下一个处理器
        return handled
    }
}

class HandlerB : HandlerList() {
    override fun handle(): Boolean {
        var handled = false
        //doAction...
        handled = Random.nextBoolean()
        println("${javaClass.simpleName}处理逻辑... 处理结果:$handled")
        //...
        //如果处理不了,交给下一个处理器
        return handled
    }
}


//处理器链
class HandlerChainB {
    private val chainList = mutableListOf<HandlerList>()

    fun addHandle(handler: HandlerList) {
        chainList.add(handler)
    }

    fun handle() {
        for (filter in chainList) {
            if (!filter.handle()) {
                return
            }
        }
    }
}

//调用处:
fun main() {
    val chainB = HandlerChainB()
    chainB.addHandle(HandlerA())
    chainB.addHandle(HandlerB())
    chainB.handle()
}

责任链模式可以将请求和处理分开,请求者无需知道处理流程,只关注处理结果;处理者只关注处理逻辑。两者解耦,提高了系统的灵活性和可扩展性。利用它们来提供框架的扩展点,能够让框架的使用者在不修改框架源码的情况下,基于扩展点定制化框架的功能。

传统的责任链定义是,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。比如责任链在网络请求的应用:Okhttp

责任链模式在 Okhttp 中的运用

Okhttp 是移动端网络请求的框架,它内部的网络请求实现就用到了责任链模式。具体实现方式是通过集合存储不同职责的拦截器,保证拦截器执行顺序,并通过递归调用的方式将每个拦截器串联起来,从而支持双向拦截:既能拦截客户端发送的请求,也能拦截服务器返回的响应。

getResponseWithInterceptorChain 是 Okhttp 进行网络请求,获取服务端响应结果的核心方法,会通过几个不同职责的拦截器拿到数据,源码如下:

Response getResponseWithInterceptorChain() throws IOException {
  //装载不同职责的拦截器
  List<Interceptor> interceptors = new ArrayList<>();
  interceptors.addAll(client.interceptors());  //1.这是为客户端预留的拦截器扩展,可以拿到纯净的请求
  interceptors.add(retryAndFollowUpInterceptor); //2.重试和重定向拦截器
  interceptors.add(new BridgeInterceptor(client.cookieJar()));  //3.处理请求和响应的Header,如Content-Length
  interceptors.add(new CacheInterceptor(client.internalCache())); //4.缓存拦截器,如果有缓存则直接返回缓存数据
  interceptors.add(new ConnectInterceptor(client));  //5.构建好TCP链接,确定好编/解码器的拦截器
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors()); //6.也是为客户端预留的拦截器扩展,可以拿到纯净的响应
  }
  interceptors.add(new CallServerInterceptor(forWebSocket)); //7.负责访问服务器并拿到响应结果

  Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
      originalRequest, this, eventListener, client.connectTimeoutMillis(),
      client.readTimeoutMillis(), client.writeTimeoutMillis());

  Response response = chain.proceed(originalRequest);  //8.开始拦截器链式调用
  if (retryAndFollowUpInterceptor.isCanceled()) {
    closeQuietly(response);
    throw new IOException("Canceled");
  }
  return response;
}

拦截器接口定义:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    //...
  }
}

具体实现:

public final class RealInterceptorChain implements Interceptor.Chain {
  private final List<Interceptor> interceptors;
  private final int index;

  public RealInterceptorChain(List<Interceptor> interceptors, int index) {
    this.interceptors = interceptors;
    this.index = index;
  }

  @Override public Response proceed(Request request) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next); //自定义拦截器需要调用chain.proceed继续责任链的调用,通过递归方式穿起来了
    //...
    return response;
  }
}

每个拦截器在内部会封装成 RealInterceptorChain 类型,它内部封装了网络的一些通用逻辑,这个可以忽略,内部还是调用了 intercept 方法传递给下一个拦截器,拦截器中又调用 chain.proceed 继续传递,通过递归的方式将拦截器链串联起来,达到双向拦截的效果,例如下面自定义的拦截器:

public final class CustomInterceptor implements Interceptor {
  @Override public Response intercept(Chain chain) throws IOException {
    //1.对请求进行处理,比如进行gzip压缩
    Response networkResponse = chain.proceed(requestBuilder.build());
	//2.对响应进行处理,比如gzip解压
    return networkResponse;
  }
}

总结

责任链模式是一种非常有用的设计模式,它通过链式结构来组织处理者,实现了请求的灵活处理和系统的可扩展性。


http://www.kler.cn/news/326975.html

相关文章:

  • ubuntu24.04 最好的输入法是什么?
  • 【ARM 嵌入式 编译系列 10.6 -- ARM toolchain examples】
  • 【Docker】解决Docker Engine stopped
  • cocos打包后发布web,控制台报错.plist资源下载404
  • Netty 与 WebSocket之间的关系
  • 宠物空气净化器该怎么选?希喂、美的、有哈这三款有推荐的吗?
  • 将 Go 作为脚本语言用及一些好用的包
  • 渗透测试入门学习——编写python脚本实现对网站登录页面的暴力破解
  • 自动化办公-Python中的for循环
  • 网络安全中的 EDR 是什么:概述和功能
  • 【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)
  • 计算机网络面试题——第二篇
  • java中创建jdbc应用程序
  • OJ在线评测系统 前端 完善题目提交服务 细讲异步前端请求与后端接口交互
  • 代码随想录算法训练营第34天|46. 携带研究材料、416. 分割等和子集
  • Android 12系统源码_输入系统(三)输入事件的加工和分发
  • Spring RestTemplate 升级 WebClient 导致 OutOfMemoryError
  • SQL进阶技巧:如何利用if语句简化where或join中的条件 | if条件语句的优雅使用方法
  • QT界面制作
  • 粉丝们得以一窥索菲亚罗兰奢华的90岁生日庆祝仪式! 她已完成了所有的遗愿清单 !
  • 选择租用徐州存储服务器有什么作用?
  • 大数据-149 Apache Druid 基本介绍 技术特点 应用场景
  • 2024年7月大众点评广州美食店铺基础信息
  • 2024.9.24 作业
  • Stable Diffusion 蒙版:填充、原图、潜空间噪声(潜变量噪声)、潜空间数值零(潜变量数值零)
  • 我店生活系统小程序开发功能解析
  • uniapp框架中实现文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间
  • 修改 idea 的 Terminal 命令窗口使用 git-bash
  • 得物App荣获新奖项,科技创新助力高质量发展
  • 国内ChatGPT镜像网站整理汇总【OpenAI o1/GPT 4o】-2024/10月最新