草庐IT

责任链设计模式

赵妹儿 2023-03-28 原文

在现实生活中,一个事件需要经过 多个对象 处理是很常见的场景。比如:采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请求的天数去找不同的领导签名,即员工必须记住每个领导的姓名、电话和地址信息,这使得该流程变得复杂冗余。

在计算机软件开发中也有相关的例子,比如 异常处理,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2拦截器JSPServlet 的 Filter过滤器等,都可以考虑使用 责任链(职责链)设计模式 来实现。Servlet 的 Filter过滤器的实现确实使用了 责任链设计模式

模式的定义与特点

责任链(Chain of Responsibility) 模式的定义:为了避免 请求发送者多个请求处理者 耦合在一起,于是将所有的 请求处理者 通过让 前一对象 记住 其下一个对象的引用 的方式而形成了一个 链条;当有请求发生时,可将请求沿着该 链条 传递,直到有对象处理当前请求为止。

责任链模式 中,客户只需要将请求发送到 责任链 上即可,无需关心请求的 处理 和请求的 传递过程,请求会 自动 进行传递,因此 责任链模式 将请求的 发送者 和请求的 处理者 解耦合了

责任链模式是一种 对象行为型 模式

主要优点

  1. 降低了对象之间的 耦合度。该模式使得一个对象无需知道到底是哪一个对象处理其请求,也无需知道 链条 的结构,发送者和接收者也无需拥有对方的明确信息
  2. 增强了系统的 可扩展性。可以根据需要增加新的 请求处理类,符合 开闭原则
  3. 增强了给对象指派职责的 灵活性。当工作流程发生变化,可以 动态 的改变 链条 内的成员或者调动它们的 次序,也可以 动态 的增加或者删除责任
  4. 简化了对象之间的 连接。每个对象只需持有一个指向其后继者的 引用,不需要持有其它所有处理者的 引用
  5. 责任 分化。每个类只需处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的 责任范围,符合 单一职责原则

主要缺点

  1. 不能保证请求一定会被处理。由于请求没有明确的 接收者,所有不能保证它一定会被处理,该请求可能一直传递到 链条 的末端都得不到处理
  2. 如果 责任链 比较长,请求的处理可能涉及多个处理对象,处理对象过多将会对 系统性能 产生影响
  3. 责任链 建立的 合理性 要靠 客户端 来保证,增加了客户端的 复杂性,可能会由于 责任链 的错误设置而导致 系统出错,如可能造成 循环调用(责任链的实现基于 递归)

模式的结构与实现

模式的结构

  1. 抽象处理者(Handler)角色:定义一个处理请求的 接口 或者 抽象类,包含 抽象处理方法 和一个 后继连接
  2. 具体处理者(Concrete Handler)角色:实现 抽象处理者处理方法,判断能否处理本次请求,如果可以处理则处理,否则将请求转给当前具体处理者的 后继者
  3. 客户类(Client)角色:创建处理链(即创建 具体处理者 对象),并向 链头 的具体处理者提交请求,它不关心 处理细节 和请求的 传递过程

责任链模式 的本质是解耦 请求处理,让请求在 处理链 中能进行 传递被处理;理解 责任链模式 应该理解其 模式,而不是其具体实现。责任链模式 的独到之处是将其节点处理者组合成了 链式结构,并允许节点自身决定是否进行 请求处理转发,相当于让请求 流动 起来

  • 模式结构图

  • 责任链示意图

模式的实现

  1. 抽象处理者角色-Handler
/**
 * 抽象处理者
 */
public abstract class Handler {
    // 定义后继者引用
    private Handler next;

    public Handler getNext() {
        return next;
    }

    public void setNext(Handler next) {
        this.next = next;
    }

    // 定义处理请求的抽象方法
    public abstract void handleRequest(String request);
}
  1. 具体处理者角色-ConcreteHandler1ConcreteHandler2
/**
 * 具体处理者1
 */
public class ConcreteHandler1 extends Handler{
    @Override
    public void handleRequest(String request) {
        if("one".equals(request)){ // 具体处理者1能够处理当前请求
            // 具体处理者1对当前请求进行处理
            System.out.println("具体处理者1负责处理当前请求,已处理完毕...");
        }else { // 具体处理者1不能处理当前请求
            if(this.getNext() != null){ // 当前具体处理者1存在后继者
                // 将当前请求发送给当前具体处理者1的后继者
                this.getNext().handleRequest(request);
            }else{ // 当前具体处理者1不存在后继者
                // 结束请求,返回
                System.out.println("没有任何处理者处理当前请求,调用结束...");
            }
        }
    }
}
/**
 * 具体处理者2
 */
public class ConcreteHandler2 extends Handler{
    @Override
    public void handleRequest(String request) {
        if("two".equals(request)){ // 具体处理者2能够处理当前请求
            // 具体处理者2对当前请求进行处理
            System.out.println("具体处理者2负责处理当前请求,已处理完毕...");
        }else { // 具体处理者2不能处理当前请求
            if(this.getNext() != null){ // 当前具体处理者2存在后继者
                // 将当前请求发送给当前具体处理者2的后继者
                this.getNext().handleRequest(request);
            }else{ // 当前具体处理者2不存在后继者
                // 结束请求,返回
                System.out.println("没有任何处理者处理当前请求,调用结束...");
            }
        }
    }
}
  1. 客户端角色-Client
/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        // 创建具体处理者对象,组装责任链
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        // 设置 handler1(第一个具体处理者对象) 的后继者为 handler2
        handler1.setNext(handler2);
        // 提交请求
        handler1.handleRequest("two");
    }
}
  • 程序运行结果:
具体处理者2负责处理当前请求,已处理完毕...

模式的应用场景

  1. 多个对象 可以处理 同一个请求,但具体由那个对象处理该请求在 运行时 自动确定
  2. 动态 指定一组对象处理请求,或添加新的处理者
  3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求

模式的扩展

  • 纯的 责任链模式:一个请求必须被某一个处理者对象所接收,且一个 具体处理者 对某个请求的处理只能采用 自己处理 或者 交给后继者处理 两种方式之一
  • 不纯的 责任链模式:允许出现某一个 具体处理者 对象在承担了请求的 一部分责任 之后,又将 剩余的责任 传递给 后继者,且一个请求可以最终不被任何 接收端对象 所接收(Filter过滤器属于 不纯 的责任链模式)

文章转载于:http://c.biancheng.net/view/1383.html

有关责任链设计模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  8. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

  9. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  10. Ruby:标准递归模式 - 2

    我经常迷上ruby​​的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情

随机推荐