职责链模式(Chain of Responsibility Pattern)

学校OA系统的采购审批项目:

 需求是

  1.采购员采购教学器材

  2.如果金额小于等于5000,由教学主任审批

  3.如果金额小于等于10000,由院长审批

  4.如果金额小于等于 30000, 由副校长审批

  5.如果金额超过30000,由校长审批

 

传统方案:

 碰到金额,根据数目调用相对应的采购员

传统方案解决 OA 系统审批问题分析

 1.传统方式是 :接收到一 个采购请求后,根据采购金额来调用对应的 Approver ( 审批人 完成审批)。

 2.传统方式的问题分析 : 客户端这里会使用到分支判断(比如switch) 来对不同的采购请求处理, 这样就存在如下问题 (1) 如果各个级别的人员审批金额发生变化,在客户端的也需要变化 (2) 客户端必须明确的知道 有多少个审批级别和访问

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

 

职责链模式

 职责链模式( Chain of Responsibility Pattern ),又叫 责任链模式,为请求创建了一个接收者对象的链简单示意图 。 这种模式对请求的发送者和接收者进行解耦 。

 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推

 

责任链模式结构

在这里插入图片描述

1.处理者 (Handler) 声明了所有具体处理者的通用接口。 该接口通常仅包含单个方法用于请求处理, 但有时其还会包含一个设置链上下个处理者的方法。

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

 通常情况下, 该类中定义了一个保存对于下个处理者引用的成员变量。 客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。 该类还可以实现默认的处理行为: 确定下个处理者存在后再将请求传递给它。

3.具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。

处理者通常是独立且不可变的, 需要通过构造函数一次性地获得所有必要地数据。

4.客户端(Client) 可根据程序逻辑一次性或者动态地生成链。 值得注意的是, 请求可发送给链上的任意一个处理者, 而非必须是第一个处理者。

 

责任链模式适合应用场景

 1.当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。

 2.当必须按顺序执行多个处理者时, 可以使用该模式。

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

 

实现方式

  1. 声明处理者接口并描述请求处理方法的签名。

    确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。

  2. 为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。

    该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。

    为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为。

  3. 依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:

    • 是否自行处理这个请求。
    • 是否将该请求沿着链进行传递。
  4. 客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。

  5. 客户端可以触发链中的任意处理者, 而不仅仅是第一个。 请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾。

  6. 由于链的动态性, 客户端需要准备好处理以下情况:

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

 

责任链模式优缺点

 优点:

  ✔️ 你可以控制请求处理的顺序。

  ✔️ 单一职责原则。 你可对发起操作和执行操作的类进行解耦。

  ✔️ 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

 缺点:

  ❌ 部分请求可能未被处理。
 

解决OA 系统审批

在这里插入图片描述

代码:

//请求类
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 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("请求编号:"+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("请求编号:"+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("请求编号:"+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("请求编号:"+purchaseRequest.getId()+"被" +this.name+"处理");
        }else {
            approver.processRequest(purchaseRequest);
        }
    }
}
public class Client {
    public static void main(String[] args) {
        //创建一个请求 类型-1  金额-31523 id-1
        PurchaseRequest request = new PurchaseRequest(1,31523,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(request);

        PurchaseRequest request1 = new PurchaseRequest(3,6000,2);
        viceSchoolMasterApprover.processRequest(request1);
    }
}


 
注意细节:

 1.将请求和处理分开,实现解耦,提高系统的灵活性

 2.简化了对象,使对象不需要知道链的结构

 3.性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext() 方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能

 4.调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂

 5.最佳应用场景 有多个对象可以处理同一个请求时,比如:多级请求、请假加薪等审批流程、 Java Web 中 Tomcat 对 Encoding 的处理、拦截器

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值