概念说明: 在责任链模式中,通常会有多个处理者对象(也称为处理器或者节点),它们按照某个顺序组成一个链。客户端向该链的开头发送请求,并沿着链传递直到有一个处理者能够处理该请求。每个处理者都有机会处理请求,如果一次请求不能被任何处理者处理,则请求将最终无法处理。
说明: 如果我们要实现一串复杂的规则校验,本来可能由一长串的 if-else 的判断来实现,但是如果校验规则非常复杂,使用 if-else 非常的不优雅,于是我们可以使用责任链模式来实现!
案例说明: 我们创建一个作品的实体类,我们使用责任链模式对其中的标题和内容进行校验
代码实现
1 文章实体类ArticleInfo
import lombok.Data;
@Data
public class ArticleInfo {
private String title; //文章标题
private String content; //文章内容
private String author; //文章作者
}
2 失败信息结果类RuleCheckResult
import lombok.Data;
import java.util.List;
@Data
public class RuleCheckResult {
/**
* 失败信息列表(存失败信息情况)
*/
private List<String> failedMsgList;
}
3 规则校验结果类RuleCheckContext
import org.apache.commons.collections4.CollectionUtils;
public class RuleCheckContext {
/**
* 规则检查结果(下一个链执行时,要检查上一个链的结果,看是否有错误,有错误就直接返回,不会向下传递、执行)
*/
private RuleCheckResult ruleCheckResult = new RuleCheckResult();
public RuleCheckResult getRuleCheckResult() {
return ruleCheckResult;
}
public boolean hasError() {
return CollectionUtils.isNotEmpty(ruleCheckResult.getFailedMsgList());
}
}
4 责任链模式: 文章规则校验实现
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
@Component
@Slf4j
public class RuleCheckCompont {
public RuleCheckResult checkResult(ArticleInfo articleInfo) {
RuleCheckContext ruleCheckContext = new RuleCheckContext();
//this代表当前类,加不加都一样
return this.checkTitle(articleInfo)
.andThen(this.checkContentLength(articleInfo)) //andThen,当前结果接着传递给下一个方法使用,可使RuleCheckContext对象共享,调用hasError方法时,会按照该类的成员属性创建一个Result对象
.apply(ruleCheckContext) //传入T,返回R,此时上一个链返回的对象为Function<T,T>,所以传入RuleCheckContext,返回类型也一样
.getRuleCheckResult();
}
/**
* 检验文章标题不能包含中国
*
*/
private Function<RuleCheckContext, RuleCheckContext> checkTitle(ArticleInfo articleInfo) {
// return buildCheck(new Consumer<RuleCheckContext>() { //消费模型的T和Function的T,R都一样
// @Override
// public void accept(RuleCheckContext ruleCheckContext) {
// if(ruleCheckContext.hasError()) { //如果当前失败列表有值,则直接返回,不往下传递
// return; //该return返回的是accept消费模型的无返回值的方法
// }
// String title = articleInfo.getTitle();
// if(title.contains("中国")) {
// RuleCheckCompont.this.addFailedMsg(ruleCheckContext.getRuleCheckResult(), "标题不能包含中国");
// }
// }
// });
return buildCheck(titleArticle -> {
if(titleArticle.hasError()) {
return;
}
String title = articleInfo.getTitle();
if(title.contains("中国")) {
RuleCheckCompont.this.addFailedMsg(titleArticle.getRuleCheckResult(), "标题不能包含中国");
}
});
}
/**
* 检查内容长度
*/
private Function<RuleCheckContext, RuleCheckContext> checkContentLength(ArticleInfo articleInfo) {
return buildCheck(new Consumer<RuleCheckContext>() {
@Override
public void accept(RuleCheckContext ruleCheckContext) {
if(ruleCheckContext.hasError()) {
return;
}
String content = articleInfo.getContent();
if(content.length() > 10) {
addFailedMsg(ruleCheckContext.getRuleCheckResult(), "内容长度不能大于10");
}
}
});
}
/**
* 添加失败的信息
*/
private void addFailedMsg(RuleCheckResult ruleCheckResult, String message) {
List<String> failedMsgList = ruleCheckResult.getFailedMsgList();
if(failedMsgList == null) {
failedMsgList = new ArrayList<>();
ruleCheckResult.setFailedMsgList(failedMsgList);
}
failedMsgList.add(message);
}
/**
* buildCheck可直接返回Function接口,返回该接口需要创建匿名内部类,重写apply方法(该方法可进行类型转换,把T转为R),但这里TR都一样,
* 函数内部,根据消费模型consumer接口进行消费,该接口内部的accept方法无返回值。
*/
private <T> Function<T, T> buildCheck(Consumer<T> consumer) {
// return new Function<T, T>() {
// @Override
// public T apply(T t) { //apply有返回值,所以最后返回R,也就是t
// consumer.accept(t); //Consumer的accept方法无返回值,后面调用该方法的函数重写accept方法进行执行。
// return t;
// }
// };
return t -> {
consumer.accept(t);
return t;
};
}
}
5 测试类
import com.ssm.user.designPattern.responseibilityChainPattern.ArticleInfo;
import com.ssm.user.designPattern.responseibilityChainPattern.RuleCheckCompont;
import com.ssm.user.designPattern.responseibilityChainPattern.RuleCheckResult;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest(classes = UserApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
@RunWith(SpringRunner.class)
public class ArticleCheckTest {
@Resource
private RuleCheckCompont ruleCheckCompont;
@Test
public void test() {
ArticleInfo articleInfo = new ArticleInfo();
articleInfo.setAuthor("石朔铭");
articleInfo.setTitle("标题中");
articleInfo.setContent("内容1111111111");
RuleCheckResult ruleCheckResult = ruleCheckCompont.checkResult(articleInfo);
System.out.println(ruleCheckResult.getFailedMsgList());
}
}