什么是责任链模式
责任链模式是一种用于封装流程逻辑,便于扩展开发的设计模式,在责任链模式的结构下,配合强大的spring框架,开发者可以非常随意的调整链的顺序以及任意删改。
责任链模式的作用
在日常业务开发中,你可能会常常去校验一个参数或者过滤一些对象,在各种if(判断条件){执行这个分支逻辑} 后,你的代码会越来越臃肿,时间长了接手你代码的同事可能就会出现这样的表情。
所以,责任链模式,是保证代码维护性的最佳实践方案。
责任链模式两种最佳实践
基于责任链模式实现顺序校验
需求描述
狗产品:搞个功能,参数校验,首先判断是否为空,再判断长度是否小于5,在判断这个参数是否包含不良词汇。
你:就这么简单?
狗产品:先这些吧,你先做,后续可能会有更多逻辑,上面这个校验功能后续可能还要对接第三方工具什么的,不好说。
你:我XXXXX
代码实现
定义参数校验抽象类,指定行为,确定每个链通用逻辑
package com.example.Chain.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractHandler {
protected static final Logger logger = LoggerFactory.getLogger(AbstractHandler.class);
/**
* 当前职责链的下一个链
*/
private AbstractHandler nextHandler;
public void setNextHandler(AbstractHandler nextHandler) {
this.nextHandler = nextHandler;
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
public void validate(String param) {
boolean validate = doValidate(param);
if (!validate){
return;
}
if (null!=getNextHandler()){
//递归调用
getNextHandler().validate(param);
}
}
/**
* 具体执行校验逻辑的方法
*
* @param param
*/
protected abstract boolean doValidate(String param);
}
复制代码
空判断校验链
注意笔者下方的操作,加了个Order注解控制ioc加载顺序,从而保证校验逻辑的顺序
package com.example.Chain.support;
import com.example.Chain.config.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
@Order(1)
public class EmptyCheckHandler extends AbstractHandler {
protected static final Logger logger = LoggerFactory.getLogger(EmptyCheckHandler.class);
@Override
protected boolean doValidate(String param) {
if (StringUtils.isEmpty(param)){
logger.info("当前参数为空,校验不通过");
return false;
}
return true;
}
}
复制代码
长度校验链
package com.example.Chain.support;
import com.example.Chain.config.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(2)
public class LengthCheckHandler extends AbstractHandler {
protected static final Logger logger = LoggerFactory.getLogger(LengthCheckHandler.class);
@Override
protected boolean doValidate(String param) {
if (param.length() < 5) {
logger.info("当前请求参数小于5 参数值 {}",param);
return false;
}
return true;
}
}
复制代码
不良词汇校验链
package com.example.Chain.support;
import com.example.Chain.config.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(3)
public class FuckCheckHandler extends AbstractHandler {
protected static final Logger logger = LoggerFactory.getLogger(FuckCheckHandler.class);
@Override
protected boolean doValidate(String param) {
if (param.contains("fuck")) {
logger.info("参数含不良信息,校验不通过 参数值:{}", param);
return false;
}
return true;
}
}
复制代码
责任链服务类
package com.example.Chain.service;
import com.example.Chain.config.AbstractHandler;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
@Component
public class CheckChainService {
@Resource
private List<AbstractHandler> handlerList;
private AbstractHandler headHandler;
@PostConstruct
public void initChain(){
for (int i = 0; i < handlerList.size(); i++) {
if (i==0){
headHandler=handlerList.get(0);
}else {
AbstractHandler preHanlder = handlerList.get(i-1);
AbstractHandler curHanlder = handlerList.get(i);
preHanlder.setNextHandler(curHanlder);
}
}
}
public void checkParam(String param){
headHandler.validate(param);
}
}
复制代码
测试用例
package com.example.Chain.service;
import com.example.DesignPattern.DesignPatternApplication;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DesignPatternApplication.class)
class CheckChainServiceTest {
@Resource
private CheckChainService checkChainService;
@Test
void doValidate() {
checkChainService.checkParam("");
checkChainService.checkParam("1");
checkChainService.checkParam("fuck u");
}
}
复制代码
基于责任链模式实现过滤
需求描述
狗产品:搞个功能,给定集合过滤空、小于5、包含不良词汇的词汇。
你:就这么简单?
狗产品:先这些吧,你先做,后续可能会有更多逻辑,上面这个过滤功能后续可能还要对接第三方工具什么的,不好说。不同客户要求不一样,你先做。
你:我XXXXX
代码实现
定义过滤行为接口
package com.example.Chain2.config;
import java.util.List;
public interface FilterHandler {
List<String> filterWord(List<String> param);
}
复制代码
过滤空
package com.example.Chain2.support;
import com.alibaba.fastjson.JSON;
import com.example.Chain2.config.FilterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
@Component
public class EmptyFilterHandler implements FilterHandler {
protected static final Logger logger = LoggerFactory.getLogger(EmptyFilterHandler.class);
@Override
public List<String> filterWord(List<String> param) {
logger.info("开始移除集合中为空的词汇,请求参数 {}", param);
for (int i = param.size() - 1; i >= 0; i--) {
if (StringUtils.isEmpty(param.get(i))) {
param.remove(i);
}
}
logger.info("移除空集合后的元素 {}", JSON.toJSONString(param));
return param;
}
}
复制代码
过滤小于5
package com.example.Chain2.support;
import com.example.Chain2.config.FilterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class LengthFilterHandler implements FilterHandler {
protected static final Logger logger = LoggerFactory.getLogger(FuckFilterHandler.class);
@Override
public List<String> filterWord(List<String> param) {
logger.info("开始过滤长度小于5的词汇,请求参数 {}", param);
for (int i = param.size() - 1; i >= 0; i--) {
if (param.get(i).length() < 5) {
param.remove(i);
}
}
logger.info("过滤小于5的词汇后的集合 {}", param);
return param;
}
}
复制代码
过滤包含不良词汇
package com.example.Chain2.support;
import com.example.Chain2.config.FilterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class FuckFilterHandler implements FilterHandler {
protected static final Logger logger = LoggerFactory.getLogger(FuckFilterHandler.class);
@Override
public List<String> filterWord(List<String> param) {
logger.info("开始过滤不雅词汇,请求参数 {}", param);
for (int i = param.size() - 1; i >= 0; i--) {
if (param.get(i).contains("fuck")) {
param.remove(i);
}
}
logger.info("过滤不雅词汇后的集合 {}", param);
return param;
}
}
复制代码
服务类
如下所示,这里就是基于spring应用上下文配合bean加载后置方法完成责任链工作类的加载到服务类中,从而完成更加便利的完成责任链的链式执行,如果对spring的afterPropertiesSet不了解的同学可以参考笔者之前的编写的这篇文章
# 手写spring第七章-完成便捷实现bean对象初始化和销毁方法
package com.example.Chain2.service;
import com.example.Chain2.config.FilterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class FilterWordService implements InitializingBean {
protected static final Logger logger = LoggerFactory.getLogger(FilterWordService.class);
@Autowired
private ApplicationContext applicationContext;
private List<FilterHandler> filterHandlerList=new ArrayList<>();
@Override
public void afterPropertiesSet() throws Exception {
Map<String, FilterHandler> filterHandlerMap = applicationContext.getBeansOfType(FilterHandler.class);
filterHandlerMap.forEach((k, v) -> filterHandlerList.add(v));
}
public List<String> filterWord(List<String> params) {
List<String> result = null;
for (FilterHandler filterHandler : filterHandlerList) {
result = filterHandler.filterWord(params);
if (CollectionUtils.isEmpty(result)) {
logger.info("当前参数已被掏空");
return new ArrayList<>();
}
}
return result;
}
}
复制代码
测试用例
package com.example.Chain2.service;
import com.alibaba.fastjson.JSON;
import com.example.DesignPattern.DesignPatternApplication;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DesignPatternApplication.class)
class FilterWordServiceTest {
protected static final Logger logger = LoggerFactory.getLogger(FilterWordServiceTest.class);
@Resource
private FilterWordService filterWordService;
@Test
void filterWord() {
List<String> words = new ArrayList<>();
words.add("1");
words.add("222");
words.add("fuckfuck");
words.add("hahahaha");
words.add("");
words.add("");
words.add("");
words.add("");
words.add("hello word");
words.add("michael");
List<String> strings = filterWordService.filterWord(words);
logger.info("经过整条链过滤后的集合为 {}", JSON.toJSONString(words));
}
}
复制代码
近期评论