责任链模式

什么是责任链模式

在责任链模式中,多个处理器依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条,链条上的每个处理器各自承担各自的处理职责。

责任链模式优点

责任链模式的优点在于,它可以动态地添加、删除和调整处理者对象,从而灵活地构建处理链。同时,它也避免了请求发送者和接收者之间的紧耦合,增强了系统的灵活性和可扩展性。

责任链模式实现

以购票为例,在实际购票业务场景中,用户发起一次购票请求后,购票接口在真正完成创建订单和扣减余票行为前,需要验证当前请求中的参数是否正常请求,或者说是否满足购票情况。

  1. 购票请求用户传递的参数是否为空,比如:车次 ID、乘车人、出发站点、到达站点等。
  2. 购票请求用户传递的参数是否正确,比如:车次 ID 是否存在、出发和到达站点是否存在等。
  3. 需要购票的车次是否满足乘车人的数量,也就是列车对应座位的余量是否充足。
  4. 乘客是否已购买当前车次,或者乘客是否已购买当天时间冲突的车次。
    解决前置校验需求需要实现一堆逻辑,常常需要写上几百上千行代码。并且,上面的代码不具备开闭原则,以及代码扩展性,整体来说复杂且臃肿。
    为了避免这种坏代码味道,我们可以运用责任链设计模式,对购票验证逻辑进行抽象。

1.定义责任链基本接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
* 抽象业务责任链组件
*/
public interface AbstractChainHandler<T> extends Ordered {

/**
* 执行责任链逻辑
*
* @param requestParam 责任链执行入参
*/
void handler(T requestParam);

/**
* @return 责任链组件标识
*/
String mark();
}

org.springframework.core.Ordered 是Spring框架中的一个接口,它被用于那些需要在容器中定义执行顺序的组件。在Spring中,有许多场景可能需要按照特定的顺序来执行或初始化Bean,比如拦截器(Interceptors)、过滤器(Filters)、后置处理器(PostProcessors)等。

2.定义购票过滤接口

1
2
3
4
5
6
7
8
9
10
/**
* 列车购买车票过滤器
*/
public interface TrainPurchaseTicketChainFilter<T extends PurchaseTicketReqDTO> extends AbstractChainHandler<PurchaseTicketReqDTO> {

@Override
default String mark() {
return TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name();
}
}

3.实现列车购买过滤器实现

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
79
/**
* 购票流程过滤器之验证参数必填
*
*/
@Component
public class TrainPurchaseTicketParamNotNullChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}

@Override
public int getOrder() {
return 0;
}
}

/**
* 购票流程过滤器之验证参数是否有效
* 验证参数有效这个流程会大量交互缓存,为了优化性能需要使用 Lua。为了方便理解流程,这里使用多次调用缓存
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamVerifyChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

private final TrainMapper trainMapper;
private final TrainStationMapper trainStationMapper;
private final DistributedCache distributedCache;

@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}

@Override
public int getOrder() {
return 10;
}
}

/**
* 购票流程过滤器之验证列车站点库存是否充足
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamStockChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

private final SeatMarginCacheLoader seatMarginCacheLoader;
private final DistributedCache distributedCache;

@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}

@Override
public int getOrder() {
return 20;
}
}

/**
* 购票流程过滤器之验证乘客是否重复购买
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketRepeatChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}

@Override
public int getOrder() {
return 30;
}
}

4.购票流程使用过滤器

1
2
3
4
5
6
7
8
9
10
private final AbstractChainContext<PurchaseTicketReqDTO> purchaseTicketAbstractChainContext;

@Override
@Transactional(rollbackFor = Throwable.class)
public TicketPurchaseRespDTO purchaseTickets(PurchaseTicketReqDTO requestParam) {
// 责任链模式,验证 0:参数必填 1:参数正确性 2:列车车次余量是否充足 3:乘客是否已买当前车次等
purchaseTicketAbstractChainContext.handler(TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name(), requestParam);
// ......
}

购票责任链实现原理

1.运行时获取责任链具体实现类

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

/**
* 抽象责任链上下文
*/
public final class AbstractChainContext<T> implements CommandLineRunner {

private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

/**
* 责任链组件执行
*
* @param mark 责任链组件标识
* @param requestParam 请求参数
*/
public void handler(String mark, T requestParam) {
List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
}
abstractChainHandlers.forEach(each -> each.handler(requestParam));
}

@Override
public void run(String... args) throws Exception {
Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder
.getBeansOfType(AbstractChainHandler.class);
chainFilterMap.forEach((beanName, bean) -> {
List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
abstractChainHandlers = new ArrayList();
}
abstractChainHandlers.add(bean);
List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
.sorted(Comparator.comparing(Ordered::getOrder))
.collect(Collectors.toList());
abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
});
}
}

2.初始化责任链容器

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
// CommandLineRunner:SpringBoot 启动完成后执行的回调函数
public final class AbstractChainContext<T> implements CommandLineRunner {

// 存储责任链组件实现和责任链业务标识的容器
// 比如:Key:购票验证过滤器 Val:HanlderA、HanlderB、HanlderC、......
private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

@Override
public void run(String... args) throws Exception {
// 调用 SpirngIOC 工厂获取 AbstractChainHandler 接口类型的 Bean
Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder
.getBeansOfType(AbstractChainHandler.class);
chainFilterMap.forEach((beanName, bean) -> {
// 获取 mark(责任链业务标识)的处理器集合
List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
// 如果不存在则创建一个集合
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
abstractChainHandlers = new ArrayList();
}
// 添加到处理器集合中
abstractChainHandlers.add(bean);
// 对处理器集合执行顺序进行排序
List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
.sorted(Comparator.comparing(Ordered::getOrder))
.collect(Collectors.toList());
// 存入容器等待被运行时调用
abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
});
}
}