23.java设计模式之责任链模式
基本需求
- 采购员采购教学器材
- 如果金额小于等于5000,由教学主任审批(0<=x<=5000)
- 如果金额小于等于10000,由教学主任审批(5000<x<=10000)
- 如果金额小于等于30000,由教学主任审批(10000<x<=30000)
- 如果金额超过30000,由教学主任审批(30000<x)
传统方案
- 传统方案是:接收到一个采购请求之后,根据采购的金额来调用对应的审批人完成审批
- 客户端这里会使用分值判断比如(if)来对不同的采购请求处理,这样就存在
- 如果各个级别的人员审批金额发生变化,在客户端的也需要变化
- 客户端必须明确的知道,有多少个审批级别和访问
- 对一个采购请求进行处理和审批人就存在强耦合关系,不利于代码的扩展和维护
- 可使用责任链模式解决
- 客户端这里会使用分值判断比如(if)来对不同的采购请求处理,这样就存在
基本介绍
-
责任链(Chain of Responsiblility)模式,又叫职责链模式,为请求创建了一个接收者对象的链,这种模式对请求的发送者和接收者进行解耦
-
责任链模式通常每个接收者都包含另一个接收者的引用,如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,以此类推
-
这种设计模式属于行为型模式
-
UML类图(原理)
-
说明
- Handler:抽象的处理者,定义一个处理请求的接口,同时含有另一个Handler
- ConcreteHandlerA和B:具体的处理者,处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前的请求则处理,否则就将该请求交给后继者去处理,从而形成一个职责链
- Request:含有多个属性,表示一个请求
-
UML类图(案例)
-
代码实现
-
// 请求类 @Data @AllArgsConstructor public class PurchaseRequest { // 请求类型 private Integer type; // id private Integer id; // 请求的价格 private Float price; }
-
// 处理请求的抽象父类 public abstract class Approve { // 处理者名称 protected String name; // 下一个处理者 protected Approve nextApprove; public Approve(String name) { this.name = name; } // 处理请求的抽象方法 public abstract void processApprove(PurchaseRequest purchaseRequest); public void setNextApprove(Approve nextApprove) { this.nextApprove = nextApprove; } } // 子类一 教学主任审批 class DepartmentApprover extends Approve{ public DepartmentApprover(String name) { super(name); } @Override public void processApprove(PurchaseRequest purchaseRequest) { if (null != purchaseRequest) { if (purchaseRequest.getPrice() <= 5000) { System.out.println(" 请求编号 id = " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); } else { // 使用下一个处理者处理 this.nextApprove.processApprove(purchaseRequest); } } } } // 子类二 院长审批 class CollegeApprover extends Approve{ public CollegeApprover(String name) { super(name); } @Override public void processApprove(PurchaseRequest purchaseRequest) { if (null != purchaseRequest) { if (purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice() <= 10000) { System.out.println(" 请求编号 id = " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); } else { // 使用下一个处理者处理 this.nextApprove.processApprove(purchaseRequest); } } } } // 子类三 副校长审批 class ViceSchoolMasterApprover extends Approve{ public ViceSchoolMasterApprover(String name) { super(name); } @Override public void processApprove(PurchaseRequest purchaseRequest) { if (null != purchaseRequest) { if (purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice() <= 30000) { System.out.println(" 请求编号 id = " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); } else { // 使用下一个处理者处理 this.nextApprove.processApprove(purchaseRequest); } } } } // 子类四 校长审批 class SchoolMasterApprover extends Approve{ public SchoolMasterApprover(String name) { super(name); } @Override public void processApprove(PurchaseRequest purchaseRequest) { if (null != purchaseRequest) { if (purchaseRequest.getPrice() > 30000) { System.out.println(" 请求编号 id = " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); } else { // 使用下一个处理者处理 this.nextApprove.processApprove(purchaseRequest); } } } }
-
public class Client { public static void main(String[] args) { // 创建各个级别的审批人 DepartmentApprover departmentApprover = new DepartmentApprover("DepartmentApprover"); CollegeApprover collegeApprover = new CollegeApprover("CollegeApprover"); ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("ViceSchoolMasterApprover"); SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("SchoolMasterApprover"); // 使用setXxx方法将各个级别的审批人联系起来 // 此次使用环状(首尾相连,请求可从任意一个级别进入,处理的条件需要实现闭环,否则会出现死循环,一直处理不了栈溢出 StackOverflowError) // 也可使用链状(首尾不相连,请求从首级别进入,直至最后一级,最后一级也处理不了,就会返回错误) departmentApprover.setNextApprove(collegeApprover); collegeApprover.setNextApprove(viceSchoolMasterApprover); viceSchoolMasterApprover.setNextApprove(schoolMasterApprover); schoolMasterApprover.setNextApprove(departmentApprover); // 创建请求并处理 collegeApprover.processApprove(new PurchaseRequest(1, 1, 50000f)); } }
-
springmvc源码
- 在springmvc的HandlerExecutionChain中就是用到了责任链模式
注意事项
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是在链较长时,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阈值,超过则不允许该链建立,避免出现超长链无意识的破坏系统性能
- 调试不方便,采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,例如:多级请求、请假/加薪等审批流程、JavaWeb中Tomcat对Encoding的处理、拦截器