设计模式(二十四)——责任链模式(Chain of Responsibility Pattern)

Petunia ·
更新时间:2024-09-21
· 877 次阅读

责任链模式(Chain of Responsibility Pattern) 背景

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据自己要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏等

在计算机软硬件中也有相关例子,如异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,如果用责任链模式都能很好解决。

基本介绍 定义

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,所提交的请求沿着链传递链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一一个对象。

使用场景举例 公司里面,报销个单据需要经过流程:
• 申请人填单申请,申请给经理
• 小于1000,经理审查。
• 超过1000,交给总经理审批。
• 总经理审批通过 公司里面,请假条的审批过程:
• 如果请假天数小于3天,主任审批
• 如果请假天数大于等于3天,小于10天,经理审批
• 如果大于等于10天,小于30天,总经理审批
• 如果大于等于30天,提示拒绝 开发中常见的场景 Java中,异常机制就是一种责任链模式。一个try可以对应多个catch,
当第一个catch不匹配类型,则自动跳到第二个catch. Javascript语言中,事件的冒泡和捕获机制。Java语言中,事件的处理
采用观察者模式。 Servlet开发中,过滤器的链式处理 Struts2中,拦截器的调用也是典型的责任链模式 模式分析 优点 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。 缺点 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。 对于较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。 模式的结构与实现 责任链模式的UML类图

在这里插入图片描述

责任链模式的处理流程图

在这里插入图片描述

结构分析 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接(采用聚合方式)。 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者,形成责任链。 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
:也可以将请求分离出来,客户端直接调用请求。 代码实现

抽象处理者角色

abstract class Handler { private Handler next; public void setNext(Handler next) { this.next=next; } public Handler getNext() { return next; } //处理请求的方法 public abstract void handleRequest(String request); }

具体处理者角色1

class ConcreteHandler1 extends Handler { public void handleRequest(String request) { if(request.equals("one")) { System.out.println("具体处理者1负责处理该请求!"); } else { if(getNext()!=null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } }

具体处理者角色2

class ConcreteHandler2 extends Handler { public void handleRequest(String request) { if(request.equals("two")) { System.out.println("具体处理者2负责处理该请求!"); } else { if(getNext()!=null) { getNext().handleRequest(request); } else { System.out.println("没有人处理该请求!"); } } } }

客户端调用

public class ChainOfResponsibilityPattern { public static void main(String[] args) { //组装责任链 Handler handler1=new ConcreteHandler1(); Handler handler2=new ConcreteHandler2(); handler1.setNext(handler2); //提交请求 handler1.handleRequest("two"); } }

结果

具体处理者2负责处理该请求! 实例分析 实例一——学校采购审批

采购员采购教学器材

如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000) 如果金额 小于等于 10000, 由院长审批 (5000<x<=10000) 如果金额 小于等于 30000, 由副校长审批 (10000<x30000) 传统方式实现

在这里插入图片描述
接收到一个采购请求后,根据采购金额来调用对应的 Approver (审批人)完成审批。

问题分析

客户端这里会使用到 分支判断(比如 switch) 来对不同的采购请求处理, 这样就存在如下问题:

如果各个级别的人员审批金额发生变化,在客户端的也需要变化 客户端必须明确的知道 有多少个审批级别和访问

这样 对一个采购请求进行处理 和 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护

责任链模式实现 UML类图

在这里插入图片描述

代码实现

抽象审批人

public abstract class Approver { //下一个处理者(聚合) Approver approver; // 名字 String name; public Approver(String name) { this.name = name; } //下一个处理者 public void setApprover(Approver approver) { this.approver = approver; } //处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象 public abstract void processRequest(PurchaseRequest purchaseRequest); }

系主任审批

public class DepartmentApprover extends Approver { public DepartmentApprover(String name) { super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { if(purchaseRequest.getPrice() <= 5000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }

学院审批

public class CollegeApprover extends Approver { public CollegeApprover(String name) { super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { if(purchaseRequest.getPrice() < 5000 && purchaseRequest.getPrice() <= 10000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }

副校长审批

public class ViceSchoolMasterApprover extends Approver { public ViceSchoolMasterApprover(String name) { super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { if(purchaseRequest.getPrice() < 10000 && purchaseRequest.getPrice() <= 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }

校长审批

public class SchoolMasterApprover extends Approver { public SchoolMasterApprover(String name) { super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { if(purchaseRequest.getPrice() > 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }

请求类

public class PurchaseRequest { //请求类型 private int type = 0; //请求金额 private float price = 0.0f; private int id = 0; //构造器 public PurchaseRequest(int type, float price, int id) { this.type = type; this.price = price; this.id = id; } public int getType() { return type; } public float getPrice() { return price; } public int getId() { return id; } }

客户端调用

public class Client { public static void main(String[] args) { //创建一个请求 PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1); //创建相关的审批人 DepartmentApprover departmentApprover = new DepartmentApprover("张主任"); CollegeApprover collegeApprover = new CollegeApprover("李院长"); ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("王副校"); SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("佟校长"); //需要将各个审批级别的下一个设置好 (处理人构成环形: ) departmentApprover.setApprover(collegeApprover); collegeApprover.setApprover(viceSchoolMasterApprover); viceSchoolMasterApprover.setApprover(schoolMasterApprover); schoolMasterApprover.setApprover(departmentApprover); departmentApprover.processRequest(purchaseRequest); viceSchoolMasterApprover.processRequest(purchaseRequest); } }

结果

请求编号 id= 1 被 佟校长 处理 请求编号 id= 1 被 佟校长 处理

注意
将具体的处理者的链状关系设置成环形的好处:无论从哪一个处理者开始调用,都不会出现空指针没有下一个处理者的错误。

模式应用场景

责任链模式通常在以下几种情况使用。

有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。 可动态指定一组对象处理请求,或添加新的处理者。 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。 模式扩展

职责链模式存在以下两种情况。

纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
作者:koala丶



责任链模式 设计模式

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章