设计模式(16):责任链模式

「尺有所短,寸有所长;不忘初心,方得始终。」

一、责任链模式是什么

「【定义】」:为请求创建了一个处理者对象的链。允许请求沿着处理者链进行发送, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。属于行为型模式。

通常每个接收者都包含对另一个接收者的引用。如果不能处理该请求,就将请求传给下一个接收者。递归调用

设计模式(16):责任链模式

「【主要作用】」:避免请求发送者与处理者耦合在一起,将多个对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

「【主要特征】」

  • 有多个对象共同对一个任务进行处理(比如审批链)。

  • 多个对象使用「链式存储结构」形成一条处理链,每个对象知道自己的下一个对象。

  • 一个对象对任务进行处理包含

    • 处理此任务,并结束任务

    • 处理此任务,添加一些操作后将对象传递个下一个任务。

    • 不处理此任务,直接将对象传递个下一个任务。

  • 客户端只需要「负责组装链式结构」,不需要关心最终是谁来处理了任务。

    客户端也可以「单独用其他工厂类获得预先组装好的链」,可以通过工厂模式实现单独的组装工厂,客户端只需要跟工厂类进行耦合。


「【责任链本质】」「将请求与处理解耦,让请求在处理链中能进行传递与被处理。责任链模式将节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发。」


二、责任链模式的适用场景

「【适用场景】」

  • 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定,可以使用责任链模式。

  • 可由客户端动态指定一组对象处理请求,或添加新的处理者,可以使用责任链模式。

  • 所需处理者及其顺序必须在运行时进行改变,可以使用责任链模式。

「【应用实例】」

  • 生活中各种审批链(请假,采购)
  • 各种客服的转线(人工客服按指定数字转接)
  • JS 中的事件冒泡
  • Tomcat 对 Encoding 的处理
  • Java的拦截器链
  • servlet 的 过滤器链

三、责任链模式结构

  • 「抽象处理者(Handler)角色」:声明处理请求的所有接口,包含抽象处理方法,持有对下一个处理者的引用。

  • 「基础处理者(Base Handler)角色」「是一个可选的类」, 可以将所有处理者共用的样本代码放置在其中。

    • 当使用基础处理者时,抽象处理者只需要定义行为方法。持有对下一个处理者的引用「下放到基础处理者」

    • 可以在基础处理者实现一些具体处理者的公共方法,避免代码冗余。

  • 「具体处理者(Concrete Handler)角色」:实现抽象处理者的处理方法。判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

  • 「客户类(Client)」:创建处理链,并向链头的具体处理者对象提交请求。设计模式(16):责任链模式

四、责任链模式实现方式

  • 声明抽象处理者接口,并且定义一个处理者的引用

  • 可以根据业务需要抽象处理者接口定义为一个接口,再定义一个抽象类作为基础处理者

    基础处理者持有一个存储指向链上下个处理者的引用。「可以将其设置为不可变类」

  • 创建具体处理者并实现其处理方法。每个处理者在接收到请求后必须实现以下之一

    • 处理此任务,并结束任务

    • 处理此任务,添加一些操作后将对象传递个下一个任务。

    • 不处理此任务,直接将对象传递个下一个任务。

  • 客户端可以自行组装链,「也可以单独用其他工厂类获得预先组装好的链」。客户端需要考虑

    • 链中可能只有单个链接。
    • 部分请求可能无法到达链尾。
    • 其他请求可能直到链尾都未被处理。

五、责任链模式的实现

【案例】:员工向上级请假

【案例说明】:员工向上级请假,根据请假时长需要不同的审批人审批,审批人有项目经理,项目主管,部门经理。下图是维基百科对责任链模式实现请假的一张结构图:

设计模式(16):责任链模式
  • 「抽象处理者(Handler)角色」

    /**
    * 抽象处理者(Handler)角色
    */

    public interface Leader {
    /**
    *处理请求的方法
    */

    public void handleRequest(int LeaveDays);
    }
  • 「基础处理者(Base Handler)角色」

    /**
    * 基础处理者(Base Handler)角色
    */

    @Data
    public abstract class BeasLeader implements Leader{

    /**
    * 持有对下一个处理者的引用
    */

    private Leader next;

    /**
    * 可以实现一些具体处理者的公共方法,避免代码冗余
    */

    public void method(int LeaveDays){
    System.out.println("可以在这里实现公共方法");
    }
    }
  • 「具体处理者(Concrete Handler)角色」

    /**
    * 具体处理者(Concrete Handler)角色 :项目经理
    */

    public class ManagerLeader extends BeasLeader{
    @Override
    public void handleRequest(int LeaveDays) {
    if (LeaveDays <= 3) {
    System.out.println("项目经理批准您请假" + LeaveDays + "天。");
    } else {
    getNext().handleRequest(LeaveDays);
    }
    }
    }

    /**
    * 具体处理者(Concrete Handler)角色 :项目主管
    */

    public class DirectorLeader extends BeasLeader{
    @Override
    public void handleRequest(int LeaveDays) {
    if (LeaveDays <= 7) {
    System.out.println("项目主管批准您请假" + LeaveDays + "天。");
    } else {
    getNext().handleRequest(LeaveDays);
    }
    }
    }

    /**
    * 具体处理者(Concrete Handler)角色 :部门经理
    */

    public class DepartmentManagerLeader extends BeasLeader{
    @Override
    public void handleRequest(int LeaveDays) {
    if (LeaveDays <= 15) {
    System.out.println("部门经理批准您请假" + LeaveDays + "天。");
    } else if (getNext() != null) {
    getNext().handleRequest(LeaveDays);
    } else {
    System.out.println("请假天数太多,没有人批准该假条!");
    }
    }
    }
  • 「客户端代码实现」

        public static void main(String[] args) throws InterruptedException {
    //部门经理没有后没有审批链
    BeasLeader departmentManagerLeader = new DepartmentManagerLeader();
    departmentManagerLeader.setNext(null);
    //项目主管没有后是部门经理审批
    BeasLeader directorLeader = new DirectorLeader();
    directorLeader.setNext(departmentManagerLeader);
    //项目经理没有后是项目主管审批
    BeasLeader managerLeader = new ManagerLeader();
    managerLeader.setNext(directorLeader);
    //向处理链链头的具体处理者对象提交请求
    managerLeader.handleRequest(46);
    }
  • 「案例输出结果」

设计模式(16):责任链模式

六、责任链模式的优缺点

  • 「优点」


    • 「降低了对象之间的耦合度」。责任链模式让发送者不需要知道对方的具体是哪个处理者处理请求。

    • 「增强了系统的可扩展性」。可以根据需要增加新的请求处理类,「满足开闭原则」

    • 「增强了给对象指派职责的灵活性」。当工作流程发生变化,客户端可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。

    • 「责任链简化了对象之间的连接」。每个对象只需保持一个指向其后继者的引用。

    • 「符合类的单一职责原则」。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成。

  • 「缺点」

    • 不能保证每个请求一定被处理。由于一个「请求没有明确的接收者」,该请求可能一直传到链的末端都得不到处理。

    • 当责任链比较长的时候,请求的处理可能涉及多个处理对象,系统性能受影响。

    • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会造成循环调用。

七、责任链模式和其他模式的区别

  • 【责任链模式】可以和【组合模式】结合使用。

    组合模式的树叶组件接收到请求后, 可以将请求沿包含全体父组件的链一直传递至对象树的底部。

设计模式(16):责任链模式
  • 【责任模式】的管理者(链的组合)可使用【命令模式】实现。可以对由请求代表的「同一个上下文对象」执行许多不同的操作。请求自身就是一个命令对象。可以「对由一系列不同上下文连接而成的链执行相同的操作」

八、总结

职责链模式在处理请求时一个处理者对象有两种行为和对一个请求的两种情况。

  • 「一个处理者对象有两种行为」

    • 处理请求
    • 将请求传递到下一节点
  • 「一个请求的两种情况」

    • 「纯的职责链模式」

      一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用上述两种行为之一:自己处理请求或者将请求传递到下一节点。

    • 「不纯的职责链模式」

      允许出现某一个具体处理者对象在「承担了请求的一部分责任后又将剩余的责任传给下家的情况」,且一个请求可以最终不被任何接收端对象所接收。


原文始发于微信公众号(星河之码):设计模式(16):责任链模式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/27122.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!