定义

使多个对象都有机会去处理请求。从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

责任链模式也叫做职责链模式

三个角色

责任链模式主要有以下三个角色:

  • 抽象处理者Handler:定义一个处理请求的接口,内部包含一个后继处理者的引用。
  • 具体处理者Concrete Handler:实现抽象处理者。如果能够处理请求则处理,否则就将该请求转发给它的后继处理者。
  • 客户端类Client:创建处理链,同时向链头的具体处理者对象提交请求,它并不关心处理细节和请求的传递过程。

场景示例

现在多数公司请假都通过钉钉来走审批流程,不同的公司审批的规则也不同。

笔者这里以请事假为例,以及以下审批事假规则来简单讲解责任链模式。

事假天数(最小单位:天)

审批人

1天

组长->人事经理

1天-7天(包含7天)

组长->总监->人事经理

7天以上

组长->总监->老板->人事经理

创建请求实体类

/**
 * @author zhh
 * @description 事假请求实体类
 * @date 2020-03-03 14:50
 */
public class PersonalLeaveRequest {

    /**
     * 申请人
     */
    private String applicant;

    /**
     * 天数
     */
    private Integer days;

    /**
     * 开始时间
     */
    private String startTime;

    /**
     * 理由
     */
    private String reason;

    // 此处忽略getter、setter方法

    /**
     * 天数常量
     */
    public interface Days {
        Integer ONE = 1;
        Integer SEVEN = 7;
    }
}

创建抽象处理者

/**
 * @author zhh
 * @description 审批人
 * @date 2020-03-03 14:58
 */
public abstract class Approver {

    /**
     * 后继审批人
     */
    protected Approver approver;

    /**
     * 设置后继审批人
     * @param approver 后继审批人
     */
    public void setNextApprpver(Approver approver) {
        this.approver = approver;
    }

    /**
     * 审批
     * @param request 事假请求
     */
    public abstract void approve(PersonalLeaveRequest request);
}

创建具体处理者

/**
 * @author zhh
 * @description 老板
 * @date 2020-03-03 15:11
 */
public class BossApprover extends Approver {

    @Override
    public void approve(PersonalLeaveRequest request) {
        if (StringUtils.isNotEmpty(request.getReason())) {
            System.out.println("老板审核结果: 批准");
            if (approver != null) {
                approver.approve(request);
            }
        } else {
            System.out.println("老板审核结果: 不批准");
        }
    }
}


/**
 * @author zhh
 * @description 总监
 * @date 2020-03-03 15:11
 */
public class InspectorGeneralApprover extends Approver {

    @Override
    public void approve(PersonalLeaveRequest request) {
        if (StringUtils.isNotEmpty(request.getReason())) {
            System.out.println("总监审核结果: 批准");
            if (approver != null) {
                approver.approve(request);
            }
        } else {
            System.out.println("总监审核结果: 不批准");
        }
    }
}


/**
 * @author zhh
 * @description 组长
 * @date 2020-03-03 15:07
 */
public class GroupLeaderApprover extends Approver {

    @Override
    public void approve(PersonalLeaveRequest request) {
        if (StringUtils.isNotEmpty(request.getReason())) {
            System.out.println("组长审核结果: 批准");
            if (approver != null) {
                approver.approve(request);
            }
        } else {
            System.out.println("组长审核结果: 不批准");
        }
    }
}


/**
 * @author zhh
 * @description 人事经理
 * @date 2020-03-03 15:07
 */
public class PersonnelManagerApprover extends Approver {

    @Override
    public void approve(PersonalLeaveRequest request) {
        if (StringUtils.isNotEmpty(request.getReason())) {
            System.out.println("人事经理审核结果: 批准");
            if (approver != null) {
                approver.approve(request);
            }
        } else {
            System.out.println("人事经理审核结果: 不批准");
        }
    }
}

这里需要注意一下,在每个具体的处理类当中 if (approver != null) {} 这个判断是否存在后继处理者引用的分支是必须存在的。

测试类及输出

这里的测试类也可以作为我们的客户端类。

/**
 * @author zhh
 * @description 测试类
 * @date 2020-03-03 15:23
 */
public class Test {

    public static void main(String[] args) {
        PersonalLeaveRequest request = new PersonalLeaveRequest();
        request.setApplicant("海豪");
        request.setDays(6);
        request.setStartTime("2020年03月03日15:24:37");
        request.setReason("家中有事");

        Approver groupLeaderApprover = new GroupLeaderApprover();
        Approver personnelManagerApprover = new PersonnelManagerApprover();
        Approver inspectorGeneralApprover = new InspectorGeneralApprover();
        Approver bossApprover = new BossApprover();

        Integer days = request.getDays();
        // 审批事假规则
        if (PersonalLeaveRequest.Days.ONE.equals(days)) {
            // 事假天数1天
            groupLeaderApprover.setNextApprpver(personnelManagerApprover);
        } else if (days > PersonalLeaveRequest.Days.ONE && days <= PersonalLeaveRequest.Days.SEVEN) {
            // 事假天数1天-7天(包含7天)
            groupLeaderApprover.setNextApprpver(inspectorGeneralApprover);
            inspectorGeneralApprover.setNextApprpver(personnelManagerApprover);
        } else if (days > PersonalLeaveRequest.Days.SEVEN) {
            // 事假天数7天以上
            groupLeaderApprover.setNextApprpver(inspectorGeneralApprover);
            groupLeaderApprover.setNextApprpver(bossApprover);
            bossApprover.setNextApprpver(personnelManagerApprover);
        } else {
            throw new RuntimeException("请假天数异常");
        }

        System.out.println(">>>>");
        System.out.println(
                String.format("[%s]发起请假申请, 请假天数[%s]天, 请假开始时间[%s], 请假理由[%s]", request.getApplicant(), request.getDays(),
                        request.getStartTime(), request.getReason()));
        System.out.println("<<<<");

        System.out.println("审核结果如下:");
        groupLeaderApprover.approve(request);
    }
}

测试类的输出结果如下:

 >>>>
[海豪]发起请假申请, 请假天数[6]天, 请假开始时间[2020年03月03日15:24:37], 请假理由[家中有事]
 <<<<
审核结果如下:
组长审核结果: 批准
总监审核结果: 批准
人事经理审核结果: 批准

类结构图

以上示例类的结构图如下所示

java 责任链 apache chain_设计模式

总结

适用场景

一个请求的处理需要多个对象当中的一个或者几个协作处理。

优点

  • 降低对象之间的耦合度。对请求的发送者和接收者进行解耦。
  • 责任链可以动态组合。当工作流程发生变化时,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。

缺点

  • 责任链较长时,请求的处理可能会涉及多个对象,从而影响系统的性能。