「补课」进行时:设计模式(9)——在请假中使用的责任链模式

546 阅读2分钟

1. 前文汇总

「补课」进行时:设计模式系列

2. 请假

作为一位新时代的社畜,我们每天起得比鸡早,睡得比狗晚,还时不时的要受到上司的 PUA ,每天都生活在水深火热之中。

生活中总会有各种意外,比如生病了,需要去医院看病,那我们需要请假去医院,一般在公司中,请假的时长和审批领导息息相关,如果这个规则是这样的:

  • 请假 3 天内小组长可以审批
  • 请假 5 天内需要大组长神品
  • 请假 20 天内需要部门经理审批

如果按照顺序思维来写程序的话,那么我们需要做大量的 if...else 的判断,并且所有的类都要耦合在一起,这时,我们可以使用责任链模式,上面的审批流成如下:

我们可以先定义一个员工类:

public interface IPerson {
    // 获取当前请假天数
    int getDays();
    // 获取审批结果
    String getResult();
}

public class Person implements IPerson {

    private int days;
    private String message;

    public Person(int days) {
        this.days = days;
        this.message = "领导,我想请 " + days + " 天假!!!";
    }

    @Override
    public int getDays() {
        return this.days;
    }

    @Override
    public String getResult() {
        return this.message;
    }
}

创建一个抽象 Handler 类:

public abstract class Handler {

    public final static int MIN = 3;
    public final static int MIDDLE = 5;
    public final static int MAX = 20;

    // 当前能处理的级别
    private int days;
    // 责任传递,定义下一个责任人
    private Handler nextHandler;
    // 所有的类都需要定义自己的能处理的请假天数
    public Handler(int days) {
        this.days = days;
    }

    public final void handleMessage(IPerson person) {
        if (this.days > person.getDays()) {
            this.response(person);
        } else {
            if (this.nextHandler != null) {
                this.nextHandler.handleMessage(person);
            } else {
                System.out.println("员工想要请假 " + this.days + " 天,超过可以审批的最大权限,那就不批了");
            }
        }
    }

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

    protected abstract void response(IPerson person);
}

这个类中最核心的部分是定义了 nextHandler 下一个责任人,如果当前的员工请假的请求不属于自己的审批范畴,则会将这个请求转发至下一个审批人。

接下来是三位具体的审批人:

public class Leader1 extends Handler {
    // 小组长
    public Leader1() {
        super(Handler.MIN);
    }

    @Override
    protected void response(IPerson person) {
        System.out.println("-----------向小组长请示------------");
        System.out.println(person.getResult());
        System.out.println("-----------请示通过---------------");
    }
}

public class Leader2 extends Handler {
    // 大组长
    public Leader2() {
        super(Handler.MIDDLE);
    }

    @Override
    protected void response(IPerson person) {
        System.out.println("-----------向大组长请示------------");
        System.out.println(person.getResult());
        System.out.println("-----------请示通过---------------");
    }
}

public class Leader3 extends Handler {
    // 部门经理
    public Leader3() {
        super(Handler.MAX);
    }

    @Override
    protected void response(IPerson person) {
        System.out.println("-----------向部门经理请示--------------");
        System.out.println(person.getResult());
        System.out.println("-----------请示通过---------------");
    }
}

然后我们来写一个测试类:

public class Test {
    public static void main(String[] args) {

        Random random = new Random();

        ArrayList<IPerson> personList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            personList.add(new Person(random.nextInt(30)));
        }

        Handler leader1 = new Leader1();
        Handler leader2 = new Leader2();
        Handler leader3 = new Leader3();

        leader1.setNextHandler(leader2);
        leader2.setNextHandler(leader3);

        for (IPerson person: personList) {
            leader1.handleMessage(person);
        }
    }
}

执行结果如下:

-----------向部门经理请示--------------
领导,我想请 17 天假!!!
-----------请示通过---------------
员工想要请假 20 天,超过可以审批的最大权限,那就不批了
-----------向小组长请示------------
领导,我想请 0 天假!!!
-----------请示通过---------------
-----------向部门经理请示--------------
领导,我想请 11 天假!!!
-----------请示通过---------------
-----------向大组长请示------------
领导,我想请 4 天假!!!
-----------请示通过---------------

3. 责任链模式

责任链模式定义如下:

Avoid coupling the sender of a request to its receiver by giving more thanone object a chance to handle the request.Chain the receiving objects andpass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)

责任链模式的重点是在 「链」 上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果,这个链类似于一个单向链表的数据结构,从开始一直向后迭代,知道找不到下一个为止,它的通用类图如下:

通用代码如下:

3.1 抽象 Handler

public abstract class Handler {
    private Handler nextHandler;

    // 每个处理者都必须对请求作出处理
    public final Response handleMessage(Request request) {
        Response response = null;
        // 判断当前处理级别
        if (this.getHandlerLevel().equals(request.getLevel())) {
            response = this.echo(request);
        } else {
            // 判断是否有下一个处理者
            if (this.nextHandler != null) {
                response = this.nextHandler.handleMessage(request);
            } else {
                // 没有匹配的业务处理者,逻辑根据具体场景实现
            }
        }
        return response;
    }
    // 设置下一个处理者
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    protected abstract Level getHandlerLevel();

    protected abstract Response echo(Request request);
}

抽象的处理者实现三个职责:

  • 定义一个请求的处理方法 handleMessage ,唯一对外开放的方法。
  • 定义一个链的编排方法 setNext ,设置下一个处理者。
  • 定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别 getHandlerLevel 和具体的处理任务 echo 。

3.2 具体处理者

public class ConcreteHandler1 extends Handler {
    @Override
    protected Level getHandlerLevel() {
        // 设置自己的处理级别
        return null;
    }

    @Override
    protected Response echo(Request request) {
        // 设置处理的业务功能
        return null;
    }
}

public class ConcreteHandler2 extends Handler {
    @Override
    protected Level getHandlerLevel() {
        return null;
    }

    @Override
    protected Response echo(Request request) {
        return null;
    }
}

public class ConcreteHandler3 extends Handler {
    @Override
    protected Level getHandlerLevel() {
        return null;
    }

    @Override
    protected Response echo(Request request) {
        return null;
    }
}

定义三个具体的处理者,以便组成一个链。

3.3 其余相关代码

public class Level {
}

public class Request {
    public Level getLevel() {
        return null;
    }
}

public class Response {
}

3.4 测试类

最后是一个测试类:

public class Test {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        Handler handler3 = new ConcreteHandler3();

        handler1.setNextHandler(handler2);
        handler2.setNextHandler(handler3);

        Response response = handler1.handleMessage(new Request());
    }
}

在实际的使用过程中,最后都会有一个封装类对责任链模式进行封装,用来取代我们现在的这个测试类,直接返回链中的第一个处理者,具体链的设置不需要高层次模块关系,这样,更简化了高层次模块的调用,减少模块间的耦合,提高系统的灵活性。

4. 优点

任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌(例如在 J2EE 项目开发中,可以剥离出无状态 Bean 由责任链处理),两者解耦,提高系统的灵活性。

5. 缺点

责任链有两个非常显著的缺点:

  • 性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。
  • 调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。

文章持续更新,可以微信搜一搜「 极客挖掘机 」第一时间阅读,回复关键字有我准备的各种教程,欢迎阅读。