实现过滤或校验逻辑的最优方案——基于spring框架下的责任链模式
什么是责任链模式
责任链模式是一种用于封装流程逻辑,便于扩展开发的设计模式,在责任链模式的结构下,配合强大的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)); } }
作者:鲨鱼吃辣椒
链接:https://juejin.cn/post/7026400720085581861