设计模式——责任链模式

228 阅读6分钟

一、概述

责任链模式为请求创建了一个接收者对象的链,如果一个对象不能处理这个请求,那么它会传递到下个接收者,这样以此类推;当一个接收对象处理了这个请求,那么这次请求就结束,并不会再继续传递下去了。有代表性的就是Java中的catch语句块,当try语句中抛出异常后,只有捕获到相应异常的catch块才会执行,其他的catch块并不会被执行到。

责任链模式避免了请求者和接收者耦合在一起,这就使得多个对象都有可能接收到请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。责任链上的接收者负责处理请求,客户端只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递。对于到try-catch语句块上,try如果出现了异常,那么try语句就只需要将异常抛出到catch中的异常链上,无需知道异常是怎么处理以及传递的,剩下的就交给异常链上的各个接收者来处理这个异常。

责任链需要2个核心的角色:

  • 抽象处理者角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对后继接收者的引用。这个角色通常由一个Java抽象类或者接口实现。
  • 具体处理者角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给后继接收者。由于具体处理者持有对后继的引用,因此,如果需要,具体处理者可以访问后继的接收者。

下面,我们就以在公司请假流程来做说明。公司请假一般是1-2天只需要直系领导(小组领导)批准就可以,超过2天的话,直系领导是没有权限给你批准的,所以把这次的请假流程传递给部门领导;3天-5天需要部门领导批准,同样超过5天,部门领导也没有权限批准,把流程传递给总监;5-7天需要总监批准,超过7天的,总监没有权限批准,所以就需要传递到老板那里;超过7天的就需要老板批准。

二、使用

根据上面的逻辑,抽象出一个领导处理请假的一个接口,这样就不用为每个领导专门写一个处理方法。所有的领导都需要实现这个接口,在处理方法中实现各自不同的逻辑。

public abstract class Leader {

    //后继接收者(领导)
    private Leader nextLeader;

    public Leader getNextLeader(){
        return nextLeader;
    }

    public void setNextLeader(Leader nextLeader){
        this.nextLeader = nextLeader;
    }

    //处理请假
    public abstract void handle(Leave leave);

}

在这个接口中都会有一个领导的领导,即后继的接收者的引用,并且有一个重要方法——handle(),当当前领导不具有处理权限时,后继领导引用将会负责处理——调用后继接收者的handle方法。

/**
 * 直系领导(小组领导)
 */
public class GroupLeader extends Leader{

    @Override
    public void handle(Leave leave) {
        if(leave.getDay() <= 2 && leave.getDay() > 0){
            Log.e(ChainOfResponsibilityModeActivity.Tag, "直系领导同意" + leave.getName() + "的请假");
        }else{
            Log.e(ChainOfResponsibilityModeActivity.Tag, "直系领导没有权限批准" + leave.getName() + "的请假");
            getNextLeader().handle(leave);
        }
    }
}
/**
 * 部门领导
 */
public class DepartmentLeader extends Leader{

    @Override
    public void handle(Leave leave) {
        if(leave.getDay() < 5 && leave.getDay() >= 3){
            Log.e(ChainOfResponsibilityModeActivity.Tag, "部门领导同意" + leave.getName() + "的请假");
        }else{
            Log.e(ChainOfResponsibilityModeActivity.Tag, "部门领导没有权限批准" + leave.getName() + "的请假");
            getNextLeader().handle(leave);
        }
    }
}
/**
 * 总监
 */
public class DirectorLeader extends Leader{

    @Override
    public void handle(Leave leave) {
        if(leave.getDay() < 7 && leave.getDay() >= 5){
            Log.e(ChainOfResponsibilityModeActivity.Tag, "总监同意" + leave.getName() + "的请假");
        }else{
            Log.e(ChainOfResponsibilityModeActivity.Tag, "总监同意没有权限批准" + leave.getName() + "的请假");
            getNextLeader().handle(leave);
        }
    }
}
/**
 * 老板
 */
public class BossLeader extends Leader{

    @Override
    public void handle(Leave leave) {
        if(leave.getDay() >= 7){
            Log.e(ChainOfResponsibilityModeActivity.Tag, "老板同意" + leave.getName() + "的请假");
        }
    }
}

老板是最大的领导,所以他没有后继接收者,所以请假这个请求始终会被接收处理,在这里我们假设领导都是大好人,都不会为难我们 (o(∩_∩)o) 有了接收者,最后还剩下请假的发起者,要有请假人、请假原因、请假天数等,我们写个实体类来封装这些参数。

public class Leave {

    //请假人
    private String name;
    //请假原因
    private String reason;
    //请假天数
    private int day;

    public Leave(String name,String reason,int day){
        this.name = name;
        this.reason = reason;
        this.day = day;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public String toString() {
        return "本人" + name + ",由于:" + reason + ",需要请假:" + day + "天";
    }
}

万事俱备,接下来测试一下。首先设置了各个领导的后继接收者,即设置领导的领导,在打印出发起者的请假详情之后,开始交给这些领导链上去处理。得到的输出如下图所示。

GroupLeader groupLeader = new GroupLeader();
DepartmentLeader departmentLeader = new DepartmentLeader();
DirectorLeader directorLeader = new DirectorLeader();
BossLeader bossLeader = new BossLeader();
groupLeader.setNextLeader(departmentLeader);
departmentLeader.setNextLeader(directorLeader);
directorLeader.setNextLeader(bossLeader);

Leave zhangLeave = new Leave("张三","临时有事",3);
Log.e(Tag, zhangLeave.toString());
groupLeader.handle(zhangLeave);
Leave liLeave = new Leave("李四","想要出去玩",7);
Log.e(Tag, liLeave.toString());
groupLeader.handle(liLeave);

从上图中我们看到,张三请假3天,直系领导是没有权限批准的,只有交给部门领导处理;而李四请假7天,除了老板之外,没有人有这种权限能够处理。

再来看看try-catch语句的责任链模式,下面没有完全列举出所有的异常。我们这里只说明try语句中会产生异常的情况,当没有异常产生的话,就不会有请求产生,没有任何意义。try中产生异常后会将这个异常请求抛出到我们的异常链中,就是下面所有的catch语句,并且当一个异常被接收处理后,这次的请求就结束了,不会继续向下传递。最后面的catch语句就是我们上个例子中的BossLeader,它的责任是最大的,所以必须要将它放在最后,否则这个老板就会处理很多的事情,不然他招那么多员工干嘛。

try{
    int a = Integer.parseInt("aaa");
}catch (NullPointerException e){
    Log.e(Tag, "空指针异常");
}catch (IllegalArgumentException e){
    Log.e(Tag, "非法参数异常");
}catch (ClassCastException e){
    Log.e(Tag, "类型转化异常");
}catch (IndexOutOfBoundsException e){
    Log.e(Tag, "下标越界异常");
}catch (ArithmeticException e){
    Log.e(Tag, "算术异常");
}catch (Exception e){
    Log.e(Tag, "发生异常");
}

三、总结

在责任链模式中,请求者和处理者是解耦的,提供了代码的灵活性与扩展,比如增加了一个董事长这个领导,无需改动原来的逻辑,只需扩展一个新的领导类即可。责任链模式也是有缺陷的,我们的责任链不一定能保证所有的情况都考虑到,所以有可能会漏掉处理请求的可能性,并且如果处理链形成了一个闭合的环,那么请求则会无限传递下去;还有一点就是需要遍历的链太多会影响性能,在递归模式中尤其要注意。

github地址:github.com/leewell5717…

四、参考

Java设计模式系列-责任链模式