设计模式之责任链模式

图1

责任链模式的说明

责任链模式:(chain of responsibility)也称职责链模式,是同一件事情交由多人去处理,多人按照一定的顺序组成一条链,这条链上的所有人都可以对事件进行拦截,直接处理事件,不再往下传递;如果有返回值,而会按照反向的过程来将返回值向上传递。责任链中的每一个成员有两个选择,要么对事件进行处理,要么把事件交由自己的下家来进行处理,而不能自己处理了一半,再将由下家处理。

责任链解决了请求任务端和具体解决任务端的耦合问题,请求任务端只需要将任务请求发送出去,而不关心具体是谁处理的。同时,责任链的成员关系也非常的灵活,新增和删除相应的成员,改变成员的层级结构也都是可以的。

责任链模式UML图

img

这里的结构相对简单,一个Handler(抽象责任链对象),若干个具体的责任链对象和一个发起事件处理请求的客户端三个角色组成。角色之间的关系有:

  1. 抽象责任链对象会接收客户端传来的事件处理请求,来让链条动起来。
  2. 抽象责任链对象中的handleRequest会处理具体的业务逻辑,由它决定任务由谁处理,且链条的递归逻辑也是在此完成的。
  3. 具体责任链对象主要是完成具体接收到事件后的处理方式。
  4. 客户端需要将各个具体的责任对象组成一条链,并创建需要处理的事件,调用handleRequest让链条动起来。

Handler(抽象责任对象)

此对象为抽象类,此对象会持有下一个责任对象的引用;一般都会有一个处理事件的方法handleRequest(Request request),接收一个事件对象。对于handleRequest内逻辑的处理一般有两种方式,一种会统一由父类来进行处理,提供相应的抽象方法让子类去实现即可。这种由父类来控制逻辑的方式,是结合了模板方法的扩展。另一种方式是整个方法由子类去实现 ,父类不负责逻辑的控制。

public abstract class Handler {
    private Handler nextHandler;

    public void handleRequest(Request request) {
        if (request != null) {
            if (canLeaveDay() >= request.getLeaveDay()) {
                handle(request);
            } else {
                if (nextHandler != null) {
                    nextHandler.handleRequest(request);
                } else {
                    System.out.println("请假天数太长,原则上是不能通过的");
                }
            }
        }
    }

    protected abstract void handle(Request request);

    /**
     * 能批准几天的假期
     *
     * @return
     */
    protected abstract int canLeaveDay();
}

这里采用的是第一种处理的方式,父类控制了业务逻辑,子类的实现相对就比较简单,只需要实现业务逻辑内必要的方法即可。

ConcreteHandler(具体责任对象)

具体责任对象,只需实现抽象类中的方法即可。

public class HeadMan extends Handler {
    @Override
    protected void handle(Request request) {
        System.out.println(request.getName() + request.getLeaveDay() + "天的假期已经被【组长】给批准了");
    }

    @Override
    protected int canLeaveDay() {
        return 2;
    }
}

public class HeadManager extends Handler {
    @Override
    protected void handle(Request request) {
        System.out.println(request.getName() + request.getLeaveDay() + "天的假期已经被【总经理】给批准了");
    }

    @Override
    protected int canLeaveDay() {
        return 10;
    }
}


public class DepartManager extends Handler {
    @Override
    protected void handle(Request request) {
        System.out.println(request.getName() + request.getLeaveDay() + "天的假期已经被【部门经理】给批准了");
    }

    @Override
    protected int canLeaveDay() {
        return 5;
    }
}

这里定义了三个具体的责任对象,按照不同的职位,等级对在职责内的请假事件进行了处理。

Request事件对象

这个对象是对需要处理事件的包装。

public class Request {
    private String name;  //姓名
    private int leaveDay;  //请假天数

    public Request(String name, int leaveDay) {
        this.name = name;
        this.leaveDay = leaveDay;
    }
 }

这里主要使用请假天数来决定由哪个领导来进行批假。

client场景类

使用者的主要任务是创建相应的责任对象,然后将其串进来。

public static void main(String[] args) {
    Handler headMan = new HeadMan();
    Handler departManager = new DepartManager();
    Handler headManager = new HeadManager();

    headMan.setNextHandler(departManager);
    departManager.setNextHandler(headManager);

    Request threeReq = new Request("zp", 3);
    headMan.handleRequest(threeReq);

    Request fiveReq = new Request("simon", 5);
    headMan.handleRequest(fiveReq);

    Request elevenReq = new Request("allen", 11);
    headMan.handleRequest(elevenReq);
}

这里创建了三个具体的责任对象,然后将其以一定的方式串连在一起,并创建了三组请求来分别验证责任链上的处理流程。下面是执行后的结果:

zp3天的假期已经被【部门经理】给批准了
simon5天的假期已经被【部门经理】给批准了
请假天数太长,原则上是不能通过的

到这里,一个标准的责任链例子就处理完成了。

责任链模式的总结

优点

  1. 使用者与事件具体的执行者解耦,使用者只需要关心责任对象之间的关系,而不需要关心每个责任对象内部的实现逻辑。
  2. 责任链上的对象新增或者移除都不影响责任链逻辑的执行。扩展性强。

缺点

  1. 处理具体事件的对象增多,原来只需要在一个对象里写的逻辑分散到了多个责任对象中,每个责任对象处理了一部分的事件。
  2. 事件有可能不会被处理

适合场景

  1. 有多个对象可以处理同一个事件,但具体由谁来处理是不确定的情况。
  2. 当不确定将由谁来处理,需要交由任意一个对象都能够处理时。