设计模式 (二十二) 责任链模式

目录

一、概念

二、责任链模式案例

三、总结


一、概念

责任链模式的定义如下:

多个请求接收者串成一条链式结构,一个请求到达请求链上,如果该接收者不能处理请求则会将请求继续往下传递(通常每个接收者都包含对另一个接收者的引用),以此类推,直到找到有一个请求接收者能够处理该请求。职责链模式将请求发送者和请求接收者进行了解耦,此种模式属于行为型模式。

责任链模式的重点是在"链"上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,其通用类图如下:

角色分析:

  • IRequestHandler:抽象处理者角色,可以是接口或者抽象类,里面聚合了抽象处理者本身,定义了具体请求处理者处理请求的方法以及获取、设置下一个请求处理者的方法;
  • ConcreateRequestHandler:具体请求接收者角色,实现了抽象请求接收者中定义的抽象方法,接收到请求后,如果可以处理该请求就处理,如果不能处理就交给请求调用链中下一个请求接收者进行处理;
  • Client:客户端,是请求发送的对象;

二、责任链模式案例

考虑以下这样的场景:

你在公司需要请假,当你请假一天的时候,这时候自己的直接主管就能完成审批;如果请假超过一天但小于三天的时候,这时候直接主管就不能审批了,此时需要交给部门主管才能审批,同样的,当请假天数超过三天时,部门主管也不能审批了,此时需要交给总经理进行审批。

类似这种流程审批的场景,在工作中比较常见,此时责任链模式将非常适合这种场景。

类图如下:

首先我们需要定义个请假请求对象:

/**
 * 请求请求
 */
public class Request {
    /**
     * 请假人姓名
     */
    private String name;
    /**
     * 请假天数
     */
    private int day;

    public Request(String name, int days) {
        this.name = name;
        this.day = days;
        System.out.println(name + "发起申请,需要请假" + days + "天");
    }

    public int getDay() {
        return day;
    }

    public String getName() {
        return name;
    }
}

接下来就需要定义一个抽象的处理者对象:

/**
 * 抽象处理器
 */
public abstract class AbstractHandler {
    /**
     * 下一个处理节点
     */
    private AbstractHandler nextHandler = null;

    /**
     * 记录每种角色能够审批的最大请假天数
     *
     * @return
     */
    public abstract int getLimit();

    /**
     * 每种角色名称
     */
    public abstract String getHandlerName();

    public void setNextHandler(AbstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     * 具体执行方法
     *
     * @param request
     */
    public final void handle(Request request) {
        if (getLimit() >= request.getDay()) {
            System.out.println(getHandlerName() + "审批请假请求");
        } else {
            if (this.nextHandler != null) {
                //交给链中的下一个处理器进行处理
                this.nextHandler.handle(request);
            } else {
                System.out.println("请假天数太长,无法审批");
            }
        }
    }

}

抽象处理者主要包含了一个自身的引用、设置下一个处理者对象的方法、以及具体执行方法:

/**
 * 抽象处理器
 */
public abstract class AbstractHandler {
    /**
     * 下一个处理节点
     */
    private AbstractHandler nextHandler = null;

    /**
     * 记录每种角色能够审批的最大请假天数
     *
     * @return
     */
    public abstract int getLimit();

    /**
     * 每种角色名称
     */
    public abstract String getHandlerName();

    public void setNextHandler(AbstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     * 具体执行方法
     *
     * @param request
     */
    public final void handle(Request request) {
        if (getLimit() >= request.getDay()) {
            System.out.println(getHandlerName() + "审批请假请求");
        } else {
            if (this.nextHandler != null) {
                //交给链中的下一个处理器进行处理
                this.nextHandler.handle(request);
            } else {
                System.out.println("请假天数太长,无法审批");
            }
        }
    }

}

有了抽象处理者,下面就需要定义几个具体的请求处理者,在此例中,就是直接主管审批、部门经理审批、总经理审批这几个处理者,以便可以组成一条链:

public class DirectLeaderHandler extends AbstractHandler {

    @Override
    public String getHandlerName() {
        return "直接主管";
    }

    @Override
    public int getLimit() {
        return 1;
    }

}

public class DeptManagerHandler extends AbstractHandler {

    @Override
    public String getHandlerName() {
        return "部门经理";
    }

    @Override
    public int getLimit() {
        return 3;
    }
}


public class ManagerHandler extends AbstractHandler {
    @Override
    public String getHandlerName() {
        return "总经理";
    }

    @Override
    public int getLimit() {
        return 30;
    }
}

下面我们定义一个场景类:

/**
 * 场景类
 */
public class Client {
    public static void main(String[] args) {
        AbstractHandler employee = new DirectLeaderHandler();
        AbstractHandler leader = new DeptManagerHandler();
        AbstractHandler manager = new ManagerHandler();

        //将链串起来
        employee.setNextHandler(leader);
        leader.setNextHandler(manager);

        employee.handle(new Request("张三", 5));
        System.out.println("===================");
        employee.handle(new Request("李四", 2));
        System.out.println("===================");
        employee.handle(new Request("王五", 50));
    }
}

运行结果:

张三发起申请,需要请假5天
总经理审批请假请求
===================
李四发起申请,需要请假2天
部门经理审批请假请求
===================
王五发起申请,需要请假50天
请假天数太长,无法审批

可见,根据不同的请求,将会找到不同的具体请求接收者去处理,如果当前接收者不能处理,则继续往下找下一个能处理请求的接收者,如果到最后都找不到,则抛出不能处理异常或者直接返回,这就是职责链模式。 

在实际应用中,一般会有一个封装类堆责任链模式进行封装,也就是替代Client场景类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。

三、总结

责任链模式的优点:

  • 避免了过多的if - else判断语句连成一条调用链来处理请求;
  • 解耦了请求发起者和请求接收者,请求者可以不用知道是谁处理的,处理者也不用知道请求的全貌,并且增加新的请求接收者很方便,无需改动原有代码,符合开闭原则;

责任链模式的缺点:

  • 不能保证请求一定被请求接收者处理;
  • 存在性能问题,如果调用链特别长,因为使用类似递归一样的方法去判断是否能够处理该请求;
  • 调试不方便,有可能这个请求接收者没问题了,下一个请求接收者又出问题了;

责任链模式的使用场景:

  • 有多个对象可以处理同一个请求,哪个对象处理请求在运行时刻自动确定;
  • 如果想在不明确指定接受者的情况下,向多个对象的一个提交请求;
  • Java中异常的处理,层层抛出;
  • 拦截器链的使用;
  • JS 中的事件冒泡;
  • Apache Tomcat 对 Encoding 的处理;
  • SpringMVC中拦截器的处理;

责任链模式的注意事项:

  • 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handle中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值