使用模板方法模式+责任链模式优雅校验入参

需求前言

无论是对于前端发起调用接口,抑或是内部RPC等方式调用接口,对于一个下游接口来说,众所周知,毫无疑问,一开始要做的肯定是对入参进行校验,但直接在controller写校验,或者封装一个校验方法(将多种类型的校验,如判空、判长度等),都违背了设计模式的单一职责,显得太不优雅。
文本将介绍使用模板方法模式+责任链这两种设计模式,优雅地解决入参校验问题!

因本文重要是介绍如何解决入参,故而模板方法模式和责任链模式是简单介绍

模板方法模式

模板方法模式是一种行为型设计模式,他在一个抽象类中定义一个算法(业务逻辑)的骨架,具体步骤的实现由子类提供,它通过将算法的不变部分放在抽象类中,可变部分放在子类中,让子类重写,达到代码的复用和扩展。

责任链模式

也是一种行为型设计模式,将多个对象串联成一条链,一个请求沿着该链路,让多个对象串行执行请求,直到最后一个对象为止。
它主要避免了请求发送者和接受者之间的耦合,目的是增强系统的灵活性和扩展性;

模板方法模式+责任链模式实现入参校验演示

/**
 * @date: 2025/1/2 18:39
 * @author: lvan
 * @description: 校验器枚举类,维护下一个校验器的关系
 */
public enum ValidatRelationEnum {
    
    NOT_NULL("notNull","判断是否为空校验器","age"),

    AGE("age","判断年龄校验器",null);

    private String TypeCode;
    
    private String desc;
    
    private String nextTypeCode;

    ValidatRelationEnum(String typeCode, String desc, String nextTypeCode) {
        TypeCode = typeCode;
        this.desc = desc;
        this.nextTypeCode = nextTypeCode;
    }

    public String getTypeCode() {
        return TypeCode;
    }

    public void setTypeCode(String typeCode) {
        TypeCode = typeCode;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getNextTypeCode() {
        return nextTypeCode;
    }

    public void setNextTypeCode(String nextTypeCode) {
        this.nextTypeCode = nextTypeCode;
    }
}

定义校验器关系枚举类

/**
 * @date: 2025/1/2 18:39
 * @author: lvan
 * @description: 校验器接口
 */
public interface ValidatorInteface {

    /**
     * 校验器校验方法
     *
     * @param requestDto 接口请求入参
     */
    void validat(RequestDto requestDto);

    /**
     * @return 当前校验器的类型
     */
    ValidatRelationEnum getCurrentValidatorType();

    /***
     *
     * @return 获取下一个校验器
     */
    default ValidatorInteface getNextValidator() {
        String nextTypeCode = getCurrentValidatorType().getNextTypeCode();

        if (!StringUtils.isEmpty(nextTypeCode)) {
            return ApplicationUtils.getBeanClass(nextTypeCode);
        }
        return null;

    }

}

定义校验器接口,定义的getNextValidator()是获取当前的下一个校验器,并与当前校验器建立关系;

public class AbstractValidator implements ValidatorInteface{

	private ValidatorInteface nextValidator;
	
	    @Override
    public void validat(RequestDto requestDto) {
        //校验前
        preValidat(requestDto);
        //校验方法
        doValidat(requestDto);
        //校验后
        postValidat(requestDto);

        //如果有下一个校验器,就往下执行
        if (nextValidator != null) {
            nextValidator.validat(requestDto);
        }

    }
}

抽象类实现接口,实现的validat()使用了模板方法模式,定义了preValidate、doValidat步骤方法,真正的校验方法是doValidat(),最后判断当前校验器的下一个校验器不为空就继续调用,为空就退出

public void preValidat(RequestDto requestDto) {
    throw new RuntimeException("校验前……");
}

在抽象类定义的步骤方法,为了让子类重写,可以通过抛异常的方式;

/**
 * @date: 2025/1/2 19:43
 * @author: lvan
 * @description: 空值校验器
 */
@Primary
@Service("notNull")
public class NotNullValidator extends AbstractValidator{

    @Override
    public void preValidat(RequestDto requestDto) {
        System.out.println("空值校验器校验前……");
    }

    @Override
    public void doValidat(RequestDto requestDto) {
        if(Objects.isNull(requestDto.getName())){
            System.err.println("名称不能为空!");
            throw new RuntimeException("名称不能为空!");
        }
        if(Objects.isNull(requestDto.getAge())){
            throw new RuntimeException("年龄不能为空!");
        }
    }

    @Override
    public void postValidat(RequestDto requestDto) {
        System.out.println("空值校验器校验后……");
    }

    @Override
    public ValidatRelationEnum getCurrentValidatorType() {
        //定义当前是空值校验器
        return ValidatRelationEnum.NOT_NULL;
    }
}

定义判空校验器,并通过getCurrentValidatorType()定义当前校验器的类型,便于在接口类ValidatorInteface#getNextValidator()获取当前校验器的下一个校验器;

/**
 * @date: 2025/1/2 19:43
 * @author: lvan
 * @description: 年龄校验器
 */
@Service("age")
public class AgeValidator extends AbstractValidator {

    @Override
    public void preValidat(RequestDto requestDto) {
        System.out.println("年龄校验器校验前……");
    }

    @Override
    public void doValidat(RequestDto requestDto) {
        if (requestDto.getAge() <= 25) {
            System.err.println("年龄不能小于25岁!");
            throw new RuntimeException("年龄不能小于25岁!");
        }
    }

    @Override
    public void postValidat(RequestDto requestDto) {
        System.out.println("年龄校验器校验后……");
    }

    @Override
    public ValidatRelationEnum getCurrentValidatorType() {
        //定义当前是年龄校验器
        return ValidatRelationEnum.AGE;
    }


}

年龄校验器也是如此;

@Override
public void afterSingletonsInstantiated() {
    //在启动spring容器时,就关联下一个校验器
    ValidatorInteface nextValidator = getNextValidator();
    this.nextValidator = nextValidator;
}

在抽象类中定义afterSingletonsInstantiated()用于在spring容器初始化时,建立当前校验器与下一个校验器的关系,是通过抽象类中的nextValidator进行关联;

/**
 * @date: 2025/1/2 18:39
 * @author: lvan
 * @description: 校验链
 */
@Component
public class ValidatorChain {

    public ValidatorInteface validatorInteface;

    public ValidatorChain(ValidatorInteface validatorInteface) {
        this.validatorInteface = validatorInteface;
    }

    public void validator(RequestDto requestDto) {
        //默认从空值校验器开始调,当最后一个校验器的关系枚举类的next属性为空,就代表是最后一个
        validatorInteface.validat(requestDto);
    }
}

最后定义责任链,默认第一个调用的是空值校验器

演示结果

我上例中校验条件是

if(Objects.isNull(requestDto.getName())){
            throw new RuntimeException("名称不能为空!");
        }
        if(Objects.isNull(requestDto.getAge())){
            throw new RuntimeException("年龄不能为空!");
        }
if (requestDto.getAge() <= 25) {
            throw new RuntimeException("年龄不能小于25岁!");
        }

校验通过的结果:

在这里插入图片描述

校验失败的结果

在这里插入图片描述
查看报错日志

因为我是打印
System.err.println(“年龄不能小于25岁!”);

在这里插入图片描述

还是挺简单的,完结撒花~
如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值