提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、责任链模式概念
- 二、责任链模式主要应用场景
- 三、责任链模式的优点
- 四、责任链模式的缺点
- 五、场景案例:参数校验
- 1.UML图
- 2.代码实现
- 2.1.请求体定义
- 2.2.响应体定义
- 2.3.数据类型枚举定义
- 2.4.创建数据校验处理器类
- 2.5.创建Boolean数据校验处理器
- 2.6.创建Date数据校验处理器
- 2.7.创建Integer数据校验处理器
- 2.8.创建String数据校验处理器
- 2.9.创建Float数据校验处理器
- 2.10.创建处理器链
- 3.单元测试
- 3.1.类型不匹配
- 3.2.自定义类型校验
- 3.3.存在其它未知数据类型
- 总结
前言
设计模式在工作中的应用之责任链模式
一、责任链模式概念
- 责任链模式是一种行为设计模式,它将请求的发送者和接收者解耦,将多个处理对象组成一个责任链,并沿着这条责任链依次调用处理对象,直到找到可以处理该请求的对象为止。
- 在责任链模式中,请求会被依次传递给每一个处理对象,直到其中一个处理对象能够处理该请求或者到达链的末尾。由于每一个处理对象只需要关注自己处理的请求,因此可以有效地分离系统的业务逻辑,增加代码的灵活性和可扩展性。
二、责任链模式主要应用场景
- 需要动态组合并执行多个业务处理对象的场景
- 需要对不同类型、级别、优先级或者来源的请求进行分类和处理的场景
- 有多个对象可以处理同一个请求,但具体由哪个对象处理请求,在运行时才能确定的场景
三、责任链模式的优点
- 可以有效地将请求发送者和接收者解耦
- 可以动态添加、删除或者重新排列处理对象
- 可以灵活地设置处理对象,满足不同类型、级别、优先级或者来源的请求
四、责任链模式的缺点
- 如果责任链过长,可能会导致请求的传递时间较长,降低系统性能
- 在责任链中没有明确的指令控制哪个处理对象将会处理请求,且请求会一直传递到责任链末尾,带来一定的不确定性
五、场景案例:参数校验
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);
}
总结
使用该方式可应用于动态传参的场景,判断值是否符合预期数据类型及自定义校验规则