责任链模式-参数校验

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

设计模式在工作中的应用之责任链模式

一、责任链模式概念

  • 责任链模式是一种行为设计模式,它将请求的发送者和接收者解耦,将多个处理对象组成一个责任链,并沿着这条责任链依次调用处理对象,直到找到可以处理该请求的对象为止。
  • 在责任链模式中,请求会被依次传递给每一个处理对象,直到其中一个处理对象能够处理该请求或者到达链的末尾。由于每一个处理对象只需要关注自己处理的请求,因此可以有效地分离系统的业务逻辑,增加代码的灵活性和可扩展性。

二、责任链模式主要应用场景

  • 需要动态组合并执行多个业务处理对象的场景
  • 需要对不同类型、级别、优先级或者来源的请求进行分类和处理的场景
  • 有多个对象可以处理同一个请求,但具体由哪个对象处理请求,在运行时才能确定的场景

三、责任链模式的优点

  • 可以有效地将请求发送者和接收者解耦
  • 可以动态添加、删除或者重新排列处理对象
  • 可以灵活地设置处理对象,满足不同类型、级别、优先级或者来源的请求

四、责任链模式的缺点

  • 如果责任链过长,可能会导致请求的传递时间较长,降低系统性能
  • 在责任链中没有明确的指令控制哪个处理对象将会处理请求,且请求会一直传递到责任链末尾,带来一定的不确定性

五、场景案例:参数校验

SpringBoot使用责任链模式校验Map<String,Object>的每一个v是否符合k类型,k的取值有1(字符串)2(布尔类型)3(浮点类型)4(时间类型)...

1.UML图

在这里插入图片描述

2.代码实现

2.1.请求体定义

@Data
@NoArgsConstructor
public class ParamRequest {
    
    
    /**
     * 数据类型
     */
    private Integer dataType;

    /**
     * 值
     */
    private Object value;

    // 其它限制条件
    /**
     * 是否必填
     */
    private boolean isRequired;
    /**
     * 单选
     */
    private boolean isSelected;
    /**
     * 多选
     */
    private boolean areSelected;
    /**
     * 最小值
     */
    private String minValue;
    /**
     * 最大值
     */
    private String maxValue;
    /**
     * 备注
     */
    private String remark;

    public ParamRequest(Integer dataType, Object value) {
    
    
        this.dataType = dataType;
        this.value = value;
    }

    public ParamRequest(Integer dataType, Object value, boolean isRequired) {
    
    
        this.dataType = dataType;
        this.value = value;
        this.isRequired = isRequired;
    }
}

2.2.响应体定义

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

@ApiModel("响应")
public class Result<T> implements Serializable {
    
    
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("编码:0表示成功,其他值表示失败")
    private int code = 0;
    @ApiModelProperty("消息内容")
    private String msg = "success";
    @ApiModelProperty("响应数据")
    private T data;

    public Result() {
    
    
    }

    public Result<T> ok(T data) {
    
    
        this.setData(data);
        return this;
    }

    public Result<T> okMsg(String msg) {
    
    
        this.code = 0;
        this.setMsg(msg);
        return this;
    }

    public boolean success() {
    
    
        return this.code == 0;
    }

    public Result<T> error() {
    
    
        this.code = 500;
        this.msg = "未知错误";
        return this;
    }

    public Result<T> error(int code) {
    
    
        this.code = code;
        this.msg = "未知错误";
        return this;
    }

    public Result<T> error(int code, String msg) {
    
    
        this.code = code;
        this.msg = msg;
        return this;
    }

    public Result<T> error(String msg) {
    
    
        this.code = 500;
        this.msg = msg;
        return this;
    }

    public Result<T> error(int code, T data) {
    
    
        this.code = code;
        this.data = data;
        return this;
    }

    public int getCode() {
    
    
        return this.code;
    }

    public void setCode(int code) {
    
    
        this.code = code;
    }

    public String getMsg() {
    
    
        return this.msg;
    }

    public Result<T> setMsg(String msg) {
    
    
        this.msg = msg;
        return this;
    }

    public T getData() {
    
    
        return this.data;
    }

    public void setData(T data) {
    
    
        this.data = data;
    }

    public String toString() {
    
    
        return "{\"code\":" + this.code + ",\"msg\":\"" + this.msg + '"' + ",\"data\":" + this.data + "}";
    }
}

2.3.数据类型枚举定义

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author mxf
 * @version 1.0
 * @description: 数据类型枚举
 * @date 2023/5/23
 */
@Getter
@AllArgsConstructor
public enum DataTypeEnum {
    
    

    /**
     * 布尔类型
     */
    BOOLEAN(1, Boolean.class),
    /**
     * 日期类型
     */
    DATE(2, Date.class),
    /**
     * 浮点类型
     */
    FLOAT(3, Float.class),
    /**
     * 字符串类型
     */
    STRING(4, String.class),
    /**
     * 整值类型
     */
    INTEGER(5, Integer.class);
    final static Set<Integer> DATA_TYPE_ENUM_CODE_SET;
    final static List<DataTypeEnum> DATA_TYPE_ENUM_LIST;

    static {
    
    
        DATA_TYPE_ENUM_CODE_SET = Arrays.stream(DataTypeEnum.values())
                .map(DataTypeEnum::getCode)
                .collect(Collectors.toSet());

        DATA_TYPE_ENUM_LIST = Arrays.stream(DataTypeEnum.values()).collect(Collectors.toList());
    }

    /**
     * code
     */
    private final Integer code;
    /**
     * 参数类型
     * /
    private final Class<?> clazz;

    public static boolean exitByCode(Integer code) {
        return DATA_TYPE_ENUM_CODE_SET.contains(code);
    }

    public static Class<?> getClazzByCode(Integer code) {
        return DATA_TYPE_ENUM_LIST.stream().filter(dte -> dte.getCode().equals(code)).map(DataTypeEnum::getClazz).findFirst().orElse(null);
    }
}

2.4.创建数据校验处理器类

斜体样式用于校验数据类型

import com.alibaba.fastjson.JSON;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * @author 28382
 */
public abstract class DataValidHandler<T> {
    
    

    protected DataValidHandler next;


    /**
     * 用于对下一个处理器的引用
     */
    public void setNext(DataValidHandler next) {
    
    
        this.next = next;
    }

    /**
     * 其它规则校验
     */
    public Result<Void> ruleVerification(String key, ParamRequest paramRequest) {
    
    
        // 默认实现
        return new Result<>();
    }

    private Class<?> getClazz() {
    
    
        // 使用反射获取 DataValidHandler 类中声明的泛型类型参数
        Type parameterizedType = this.getClass().getGenericSuperclass();
        return (Class<?>) ((ParameterizedType) parameterizedType).getActualTypeArguments()[0];
    }

    /**
     * 处理
     */
    public final Result<Void> handle(Map<String, ParamRequest> paramMap) {
    
    
        Iterator<Map.Entry<String, ParamRequest>> iterator = paramMap.entrySet().iterator();
        while (iterator.hasNext()) {
    
    
            Map.Entry<String, ParamRequest> entry = iterator.next();
            ParamRequest paramRequest = entry.getValue();
            if (Objects.isNull(paramRequest) || (getClazz() != DataTypeEnum.getClazzByCode(paramRequest.getDataType()))) {
    
    
                continue;
            }
            if (paramRequest.getValue() != null && !getClazz().isInstance(paramRequest.getValue())) {
    
    
                paramMap.clear();
                next = null;
                return new Result<Void>().error("Value of " + entry.getKey() + " is invalid: " + paramRequest.getValue());
            }
            Result<Void> ruleVerificationResult = ruleVerification(entry.getKey(), paramRequest);
            if (ruleVerificationResult.getCode() != 0) {
    
    
                paramMap.clear();
                next = null;
                return ruleVerificationResult;
            }
            iterator.remove();
        }

        return nextHandle(paramMap);
    }

    /**
     * @param paramMap 参数列表
     * @return com.mxf.code.chain_params.Result<java.lang.Void>
     * @author mxf
     * @description 下一个处理器
     * @createTime 2023/5/24 10:46
     * @paramType [java.util.Map<java.lang.String, com.mxf.code.chain_params.ParamRequest>]
     */
    public final Result<Void> nextHandle(Map<String, ParamRequest> paramMap) {
    
    
        Result<Void> handleResult = new Result<>();
        if (!Objects.isNull(next)) {
    
    
            handleResult = next.handle(paramMap);
        } else {
    
    
            if (!CollectionUtils.isEmpty(paramMap)) {
    
    
                return new Result<Void>().error("存在其它类型参数未被校验:" + JSON.toJSONString(paramMap));
            }
        }
        return handleResult;
    }
}

2.5.创建Boolean数据校验处理器

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @author 28382
 */
@Component
@Order(0)
public class BooleanValidHandler extends DataValidHandler<Boolean> {
    
    
    @Override
    public Result<Void> ruleVerification(String key, ParamRequest paramRequest) {
    
    
        // 判断是否必填
        boolean isRequired = paramRequest.isRequired();
        if (isRequired && Objects.isNull(paramRequest.getValue())) {
    
    
            return new Result<Void>().error("Value of " + key + " is required");
        }
        return new Result<>();
    }
}

2.6.创建Date数据校验处理器

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author 28382
 */
@Component
@Order(1)
public class DateValidHandler extends DataValidHandler<Date> {
    
    
	// 可重写ruleVerification()
}

2.7.创建Integer数据校验处理器

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author 28382
 */
@Component
@Order(2)
public class IntegerValidHandler extends DataValidHandler<Integer> {
    
    

}

2.8.创建String数据校验处理器

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author 28382
 */
@Component
@Order(3)
public class StringValidHandler extends DataValidHandler<String> {
    
    

}

2.9.创建Float数据校验处理器

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author 28382
 */
@Component
@Order(4)
public class FloatValidHandler extends DataValidHandler<Float> {
    
    

}

2.10.创建处理器链

该类使用SpringBoot提供的@Autowired注解注入所有实现了DataValidHandler接口的Bean,并按照Order注解指定的顺序对处理器链进行排序,最后将所有处理器串联起来。同时,该类也提供一个handle()方法,用于启动数据的处理。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * @author 28382
 */
@Service
public class DataValidHandlerChain {
    
    

    // 自动注入
    @Autowired
    private List<DataValidHandler> handlers;

    /**
     * 创建数据验证责任链
     */
    @PostConstruct
    public void init() {
    
    
        handlers.sort(Comparator.comparingInt(h -> getOrder(h.getClass())));
        for (int i = 0; i < handlers.size() - 1; i++) {
    
    
            handlers.get(i).setNext(handlers.get(i + 1));
        }
    }

    private int getOrder(Class<?> clazz) {
    
    
        Order order = clazz.getDeclaredAnnotation(Order.class);
        return (order != null) ? order.value() : Ordered.LOWEST_PRECEDENCE;
    }

    /**
     * 开始处理数据
     */
    public Result<Void> handle(Map<String, ParamRequest> data) {
    
    
        return handlers.get(0).handle(data);
    }
}

3.单元测试

3.1.类型不匹配

import com.mxf.code.chain_params.DataTypeEnum;
import com.mxf.code.chain_params.DataValidHandlerChain;
import com.mxf.code.chain_params.ParamRequest;
import com.mxf.code.chain_params.Result;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.HashMap;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ChainTest {
    
    

    @Autowired
    private DataValidHandlerChain handlerChain;

    @Test
    public void test01() {
    
    
        Map<String, ParamRequest> data = new HashMap<>();
        data.put("name", new ParamRequest(DataTypeEnum.STRING.getCode(), 1));
        Result<Void> handle = handlerChain.handle(data);
        log.info("handle = {}", handle);
    }
}

在这里插入图片描述

3.2.自定义类型校验

布尔类型不能为空

@Test
public void test02() {
    
    
    Map<String, ParamRequest> data = new HashMap<>();
    data.put("male", new ParamRequest(DataTypeEnum.BOOLEAN.getCode(), null, true));
    Result<Void> handle = handlerChain.handle(data);
    log.info("handle = {}", handle);
}

在这里插入图片描述

3.3.存在其它未知数据类型

@Test
public void test03() {
    
    
     Map<String, ParamRequest> data = new HashMap<>();
     data.put("subordinate",new ParamRequest(null,""));
     Result<Void> handle = handlerChain.handle(data);
     log.info("handle = {}", handle);
 }

在这里插入图片描述

总结

使用该方式可应用于动态传参的场景,判断值是否符合预期数据类型及自定义校验规则

猜你喜欢

转载自blog.csdn.net/weixin_44063083/article/details/130852557