「补课」进行时:设计模式(9)——在请假中使用的责任链模式
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 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. 缺点
责任链有两个非常显著的缺点:
性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。 调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。