一、责任链的基本概念
科普中国在百科中对责任链进行了这样的描述:“责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求”。简要流程如下图所示:
二、责任链的好处
假设有这样的一个业务场景:“客户端向服务端进行消息上报,由于客户端数据质量参差不齐,服务端需要对其进行防护,比如判断这个消息对象是否存在、判断这个消息对象的属性是否合法、判断这个消息对象xxxx等”。若使用常规方式实现:
public FunResult<String> receive(Message message) {
// 判断信息对象是否重复
if (checkRepeat(message)) {
return FunResult.<String>builder().fail().message("消息已存在, 请勿重复上报").build();
}
// 校验信息对象内字段是否合法
if (checkField(message)) {
// 这里若需要返回详细字段错误提示语, 逻辑可能会更加复杂
return FunResult.<String>builder().fail().message("消息字段校验失败, 请核实后上报").build();
}
// 这里进行一些其他的校验
if (checkXxx(message)) {
return FunResult.<String>builder().fail().message("xxxxxx").build();
}
// ... 其他的校验逻辑和业务逻辑 ...
return FunResult.<String>builder().success().message("上报信息接收成功").build();
}
常规的编写方式存在两个明显弊端:
- 代码臃肿,现实的业务场景远远比示例要复杂得多,这样就会导致这个类非常的庞大和臃肿。
- 高度耦合,如果想新增校验逻辑或者改变校验顺序只能对代码进行大面积的调整。
如果使用责任链模式对上诉流程进行重构,将每个 if…else 判断规划成对应的校验器节点,这样不仅可以提升业务代码的可读性,还可以大大降低耦合度,并且方便我们对校验顺序调整以及校验方式的扩充。
三、责任链的简单实现(其一)
通过第一节我们得知“责任链的每个节点对象通常会持有下一个对象的引用”,看到这段话大家肯定能够想到 “单向链表结构”,这一节就是参考 “单向链表结构” 来实现责任链模式。
首先,我们实现一个抽象的 “链节点” 这个链接点中包含 “下个对象的引用” 和一个 “抽象的处理方法” :
/**
* 抽象的链节点
* @author: younnng
* @param <E> 在链上传递的对象泛型
* @param <R> 期望返回的结果集泛型
*/
public abstract class ChainNode<E, R> {
/**
* 下一个节点
*/
protected ChainNode<E, R> next;
/**
* 执行处理操作
* @param element 在每个链节点上传递的对象
* @return 返回结果集
*/
public abstract R process(E element);
/**
* 给节点赋值
* @param next 下一个节点
* @return 第一个节点
*/
public ChainNode<E, R> setNext(ChainNode<E, R> next) {
this.next = next;
return next;
}
}
定义完链节点后,我们对第二节的业务场景进行简单重构,先定义 “校验节点” :
@Slf4j
public class CheckRepeatNode extends ChainNode<Message, FunResult<String>> {
@Override
public FunResult<String> process(Message message) {
log.debug("...进行重复校验...");
// ... 执行校验逻辑, 这里默认成功 ...
log.debug("...重复校验完毕, 准备传递至下一个节点...");
return this.next == null ? FunResult.<String>builder().success().message("信息校验成功").build()
: this.next.process(message);