责任链模式

责任链模式

一、定义

摘自百度百科: 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

点击查看源码


二、角色分类

抽象处理者(Handler)

定义了处理请求的接口或者抽象类,并提供了处理请求的的方法和设置下一个处理者的方法

具体处理者(Concrete Handler)

它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发

客户角色(Client)

具体调用方法的角色


三、实现方式

UML图

未命名文件 (3)

具体实现

假如我们想用责任链模式实现一个请假流程,可以这么来实现

抽象处理者(Handler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
public abstract class Handler {
// 处理者姓名
protected String processorName;
// 下一个处理者
protected Handler nextHandler

public Handler (String processorName) {
this.processorName = processorName;
}

/**
* 处理请假抽象方法
* @param name 请假人姓名
* @param numOfDays 请假天数
*/
public abstract boolean process(String name, int numOfDays);
}

具体处理者(Concrete Handler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/**
* 组长
*/
@Slf4j
public class TeamLeaderHandler extends Handler {
public TeamLeaderHandler(String processorName) {
this.processorName = processorName;
}

@Override
public boolean process(String name, int numOfDays) {
// 创建随机数,值大于3则为通过,否则为不通过
boolean res = (new Random().nextInt(10)) > 3;
String result = res ? "通过" : "驳回";
log.info("组长<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

if(Boolean.FALSE.equals(res)) {
// 审批驳回
return false;
} else if (numOfDays < 3){
// 请假通过且请假天数小于3天时直接通过
return true;
}
// 若审批通过且请假天数大于等于3天时提交给下一个审批人
return nextHandler.process(name, numOfDays);
}
}

/**
* 部门经理
*/
@Slf4j
public class DepartmentManagerHandler extends Handler {
public DepartmentManagerHandler(String processorName) {
super(name);
}

@Override
public boolean process(String name, int numOfDays) {
// 创建一个随机数 当随机数大于3时为通过,否则为不通过
boolean res = (new Random().nextInt(10)) > 3;
String result = res ? "通过" : "驳回";
log.info("部门经理<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

if(Boolean.FALSE.equals(res)) {
// 审批驳回
return false;
} else if (numOfDays <7) {
// 审批通过且审批天数小于7天时直接通过
return true;
}
// 批准天数大于等于7天时提交给下一个审批人
return nextHandler.process(name, numOfDays);
}

/**
* CEO
*/
@Slf4j
public class CEOHandler extends Handler {
public CEOHandler(String processorName) {
super(name);
}

public boolean process(String name, int numOfDays) {
// 创建一个随机数,大于3则为通过,否则为驳回
boolean res = (new Random().nextInt(10)) > 3;
String result = res ? "通过" : "驳回";
log.info("CEO<{}>审批<{}>的请假申请,请假天数为:<{}>天,审批结果为:<{}>", processorName, name, numOfDays, result);

if(Boolean.FALSE.equals(res)) {
//驳回
return false;
}
return true;
}
}
}

客户角色(Client)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Client {
public static void main(String[] args) {
Handler zhangsan = new TeamLeaderHandler("张三");
Handler lisi = new DepartmentManagerHandler("李四");
Handler wangwu = new CEOHandler("王五");

// 创建责任链
zhangsan.setNextHandler(lisi);
lisi.setNextHandler(wangwu);

// 发起请假申请
boolean res1 = zhangsan.leave("小A", 2);
System.out.println("最终结果:" + res1);

boolean res2 = zhangsan.leave("小B", 5);
System.out.println("最终结果:" + res2);

boolean res3 = zhangsan.leave("小C", 10);
System.out.println("最终结果:" + res3);
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
组长<张三>审批<小A>的请假申请,请假天数为:<2>天,审批结果为:<通过>
最终结果:true

部门经理<李四> 审批 <小B> 的请假申请,请假天数: <5>天 ,审批结果:<不通过>
最终结果:false

组长<张三> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过>
部门经理<李四> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过>
CEO<王五> 审批 <小C> 的请假申请,请假天数: <10> 天,审批结果:<通过>
最终结果:true

四、应用场景

以下部分内容摘自菜鸟教程

意图: 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

何时使用: 在处理消息的时候以过滤很多道。

如何解决: 拦截的类都实现统一接口。

关键代码: Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

应用实例:

  1. 红楼梦中的”击鼓传花”。
  2. JS 中的事件冒泡。
  3. JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。

使用场景:

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态指定一组对象处理请求。

注意事项: 在 JAVA WEB 中遇到很多应用。


五、优缺点

优点

  1. 降低耦合度。它将请求的发送者和接收者解耦。
  2. 简化了对象。使得对象不需要知道链的结构。
  3. 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 增加新的请求处理类很方便。

缺点

  1. 不能保证请求一定被接收。
  2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
  3. 可能不容易观察运行时的特征,有碍于除错。