实现过滤或校验逻辑的最优方案——基于spring框架下的责任链模式

什么是责任链模式

责任链模式是一种用于封装流程逻辑,便于扩展开发的设计模式,在责任链模式的结构下,配合强大的spring框架,开发者可以非常随意的调整链的顺序以及任意删改。

责任链模式的作用

在日常业务开发中,你可能会常常去校验一个参数或者过滤一些对象,在各种if(判断条件){执行这个分支逻辑} 后,你的代码会越来越臃肿,时间长了接手你代码的同事可能就会出现这样的表情。

图片.png

所以,责任链模式,是保证代码维护性的最佳实践方案。

责任链模式两种最佳实践

基于责任链模式实现顺序校验

需求描述

狗产品:搞个功能,参数校验,首先判断是否为空,再判断长度是否小于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));
    }
}

源码地址

gitee.com/fugongliude…

参考文献

# 在ArrayList的循环中删除元素,会不会出现问题?

# 【原理探究】ArrayList遍历时删除元素的正确姿势是什么?

# 实战!告别流水线式写代码,聊聊工作中常用的设计模式!

# 春节期间,我用责任链模式重构了业务代码

www.cnblogs.com/lm970585581…

猜你喜欢

转载自juejin.im/post/7026400720085581861