08-责任链模式

参考资料1:http://c.biancheng.net/design_pattern/

参考资料2:https://refactoringguru.cn/design-patterns/catalog

责任链图

1. 介绍

1.1 简介

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

1.2 优缺

  1. 意图
    避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
  2. 主要解决
    责任链上的处理者负责处理请求,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递,所以责任链将请求的发送者和请求的处理者解耦了。
  3. 何时使用
    在处理消息的时候以过滤很多道。
  4. 如何解决
    拦截的类都实现统一接口。
  5. 关键代码
    Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
  6. 应用实例
    • 红楼梦中的”击鼓传花”。
    • JS 中的事件冒泡。
    • JAVA WEB 中 Apache Tomcat 对 Encoding 的处理,Struts2 的拦截器,jsp servlet 的 Filter。
  7. 优点
    • 降低耦合度。它将请求的发送者和接收者解耦。
    • 简化了对象。使得对象不需要知道链的结构。
    • 增强给对象指派责任的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
    • 增加新的请求处理类很方便。
  8. 缺点
    • 不能保证请求一定被接收。
    • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
    • 可能不容易观察运行时的特征,有碍于除错。
  9. 使用场景
    • 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
    • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
    • 可动态指定一组对象处理请求。

1.3 模式结构

责任链模式主要包含以下角色:

  • 抽象处理者(Handler)角色-核心:定义一个处理请求的抽象类或接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

结构图

责任链结构图

责任链图

责任链图

2. 实现

以请假的审批流程为例。

我是小赵,我就是其中的小组长之一,我有七个手下。

今天,手下小明来找我请假,说他家里出了点状况,需要请一个月的假。

我只是小组长,我完字 → 就叫他去找部门经理,部门经理签字 → 又让他去找总经理,总经理最终签字。

这个假才算请完。

后来才知道:

  • 3天以下的假小组长1个人签字即可
  • 3-7天的假需要小组长、部门经理2个人签字
  • 7以上的假小组长、部门经理签完字后,还需要总经理总共3个人签字

企业中非常常见。

角色1:请假条,其实可以理解成请假人,因为请假条上自然有请假人信息。

角色2:领导人,小组长、部门经理、总经理都是审批人。

动作:领导人审批请假条。

  • 请假审批的责任链模式类图:

image-20200913001202141

  • 代码实现:

请假条接口:

1
2
3
4
5
6
7
8
9
10
11
/**
* @Interface: ILeave
* @Description: 请假条接口
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:02
*/
public interface ILeave {
String getName();//请假人姓名
int getNum();//请假天数
String getContent();//请假条内容
}

请假条:

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
import com.demo.designmode.chain.ILeave;

/**
* @Class: ILeaveImpl
* @Description: 请假条
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:03
*/
public class Leave implements ILeave {
private String name;//姓名
private int num;//请假天数
private String content;//请假内容

public Leave(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}

@Override
public String getName() {
return name;
}

@Override
public int getNum() {
return num;
}

@Override
public String getContent() {
return content;
}
}

处理者抽象类——责任链模式的核心:

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
/**
* @Class: Handler
* @Description: 处理者抽象类
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:13
*/
public abstract class Handler {
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
// 该领导处理的请假天数区间
private int numStart = 0;
private int numEnd = 0;
// 领导上面还有领导
private Handler nextHandler;

// 设置请假天数范围 上不封顶
public Handler(int numStart) {
this.numStart = numStart;
}

// 设置请假天数范围
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}

// 设置上级领导
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}

// 提交请假条
public final void submit(ILeave leave) {
if (0 == this.numStart) {
return;
}

// 如果请假天数达到该领导者的处理要求
if (leave.getNum() >= this.numStart) {
this.handleLeave(leave); // 执行不同的子类的方法实现

// 如果还有上级 并且请假天数超过了当前领导的处理范围
if (null != this.nextHandler && leave.getNum() > numEnd) {
this.nextHandler.submit(leave);// 继续提交
}
}
}

// 各级领导处理请假条方法
protected abstract void handleLeave(ILeave leave);
}

小组长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.demo.designmode.chain.Handler;
import com.demo.designmode.chain.ILeave;
/**
* @Class: GroupLeader
* @Description: 小组长
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:06
*/
public class GroupLeader extends Handler {
public GroupLeader() {
// 小组长处理 1-3 天的请假
super(Handler.NUM_ONE, Handler.NUM_THREE);
}

@Override
public void handleLeave(ILeave leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("小组长审批:同意。");
}
}

部门经理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.demo.designmode.chain.Handler;
import com.demo.designmode.chain.ILeave;
/**
* @Class: Manager
* @Description: 部门经理
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:07
*/
public class Manager extends Handler {
public Manager() {
// 部门经理处理 3-7 天的请假
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}

@Override
public void handleLeave(ILeave leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("部门经理审批:同意。");
}
}

总经理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.demo.designmode.chain.Handler;
import com.demo.designmode.chain.ILeave;
/**
* @Class: BigManager
* @Description: 总经理
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:07
*/
public class BigManager extends Handler {
public BigManager() {
// 总经理处理 7 天以上的请假
super(Handler.NUM_SEVEN);
}

@Override
public void handleLeave(ILeave leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("总经理审批:同意。");
}
}

客户端:

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
import com.demo.designmode.chain.impl.BigManager;
import com.demo.designmode.chain.impl.GroupLeader;
import com.demo.designmode.chain.impl.Leave;
import com.demo.designmode.chain.impl.Manager;
/**
* @Class: StartClass
* @Description: 客户端
* @Author: Jerry(姜源)
* @Create: 20/09/13 00:07
*/
public class StartClass {
public static void main(String[] args) {
// 请假条来一张
ILeave leave = new Leave("小明", 8, "身体不适");

// 各位领导
Handler groupLeader = new GroupLeader();
Handler manager = new Manager();
Handler bigManager = new BigManager();

/*
* 之所以在这里设置上级领导,是因为可以根据实际需求来更改设置
* 如果实战中上级领导人都是固定的,则可以移到领导实现类中
*/
groupLeader.setNextHandler(manager);// 小组长的领导是部门经理
manager.setNextHandler(bigManager);// 部门经理的领导是总经理

// 提交申请
groupLeader.submit(leave);
}
}

image-20200913003320498

image-20200913003401709

image-20200913002656850

3. 应用场景

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

4. 模式扩展

  1. 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
  2. 不纯的责任链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

08-责任链模式
https://janycode.github.io/2020/09/12/10_设计模式/08-责任链模式/
作者
Jerry(姜源)
发布于
2020年9月12日
许可协议