项目中运用责任链创建校验器

本文介绍了一个数据导入项目中采用责任链模式实现字段校验的过程。通过定义一系列的校验器并将其组织成链式结构,实现了对不同字段的有效性和格式进行高效校验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

近期在做一个数据导入项目,有一个模版选择,模版中可以选择对导入的字段进行效验,例如非空,长度,格式,类型等,如若放在一个类中的话,大量的臃肿代码免不了,例如“if”这样的,那就运用责任链模式,各做各的判断。看到这篇文章不明白责任链的同学们去翻我的上篇文章。

思路

用责任链设计模式来构造这个项目,每个判断做成一个校验器,将每个校验器做成链子,一个接一个的做。库中有五个校验器,假如用户是选择了两个或者一个校验器的话怎么办呢,责任链可是一条链子完整的啊?是可以保持连接,根据排序选哪个做哪个,没选这个也经过这个,只是不做操作轮给下一个,有点麻烦啊个人感觉,怎么办呢?好办,用户导入了模版传进来之后肯定会带有选择的校验器,将校验器取出来,根据选择的几个校验器创建对应的校验器不就好了,选了两个,那我这个链子就只有两条,下面从头来操作演示一遍。

搭建责任链模式

顶层接口

import java.util.HashMap;

/**
 * @Author: ChenBin
 * @Date: 2018/4/27/0027 18:53
 */
public interface Check {

    HashMap<String, Object> validate(Request request, Client client);
}

HashMap是用做返回结果的,Request 是一个对象,用来接收参数,Client是一个操作类,客观莫急,往下看,代码都有。

接口实现类

import java.util.HashMap;

/**
 * @Author: ChenBin
 * @Date: 2018/4/27/0027 18:58
 */
public class NullCheck implements Check {

    @Override
    public HashMap<String, Object> validate(Request request, Client client) {
        HashMap<String, Object> map = new HashMap<>(16);
        if (request.getRequestStr() != null && !"".equals(request.getRequestStr())) {
            map = client.validate(request, client);
        }else {
            map.put("error", "字段不能为空");
            return map;
        }
        return map;
    }
}

大致说明一下,如果传进来的这个参数不为空的话,好,过了非空这关了,去下一关,不然的话停止走到下一个校验器里去(第一关都没过还想过第二关?想什么呢),在这里我就给大家做一个校验器的操作,另外长度校验器什么的,也就是判断长度,差不多代码的。

传参类

/**
 * @Author: ChenBin
 * @Date: 2018/5/2/0002 11:28
 */
public class Request {
    private String requestStr;
    private String templateId;

    public String getTemplateId() {
        return templateId;
    }

    public void setTemplateId(String templateId) {
        this.templateId = templateId;
    }

    public String getRequestStr() {
        return requestStr;
    }

    public void setRequestStr(String requestStr) {
        this.requestStr = requestStr;
    }
}

两个属性,一个是传入的参数,一个是要传给我的ID,因为我总要知道是哪个模版,模版里有哪些校验器,我要去根据这个ID去数据库里查

场景操作类

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @Author: ChenBin
 * @Date: 2018/5/2/0002 10:29
 */
public class Client implements Check {
    /**
     * 用List集合来存储效验规则
     * */
    List<Check> checks = new ArrayList<>();
    /**
     * 用于标记规则的引用顺序
     * */
    int index = 0;
    /**
     * 往规则链条中添加规则
     * */
    public Client addCheck(Check filter) {
        checks.add(filter);
        return this;
    }

    @Override
    public HashMap<String, Object> validate(Request request, Client client) {
        HashMap<String, Object> map = new HashMap<>(16);
        //index初始化为0,checks.size()为校验器的数量,不会执行return操作
        if (index == checks.size()) {
            map.put("res", "校验结束");
            return map;
        }

        Check check = checks.get(index);
        //每添加一个过滤规则,index自增1
        index++;
        //根据索引值获取对应的规律规则对字符串进行处理
        map = check.validate(request, client);
        return map;
    }
}

这里都加了注释,我大体说明一下,创建一个List集合,因为List是有排序的,校验器也是需要根据顺序一个接着一个的,这个index就是校验器的顺序,有个addCheck方法,是将实现的几个类添加进来用的,validate是继承下来,但在里面做的操作不同,如果顺序等于添加的校验器数量,就是没校验器了,停止操作,不然继续操作校验器,这里有个checks.get(index)是根据排序我们获取对应顺序的校验器,这次是第一个校验器,下一次进来index+1了,那就是第二个校验器了。

写个类来看下效果

/**
 * @Author: ChenBin
 * @Date: 2018/5/2/0002 14:30
 */
public class Main {
    public static void main(String[] args) {
        //模拟传入的值
        String msg = "1";
        //实例化传参类
        Request request=new Request();
        //设置参入的值
        request.setRequestStr(msg);
        //实例化场景操作类
        Client client = new Client();
        //调用add方法,将需要的校验器放进去,多个的话可以在add后面“.addCheck()”继续添加
        client.addCheck(new NullCheck());
        //调用
        System.out.println(client.validate(request, client));

    }
}

打印结果

{res=校验结束}

这是非空的结果,下面看空的结果

{error=字段不能为空}

测试没问题,那接下来操作导入时候的如何使用校验器了

由于要根据参数的模版ID来查找数据库,找到在数据库对应的校验器,拿到校验器的className,各位知道,我们不能在测试里一样直接new的,这就写死了,根据ID找到数据库里字段的类完整包路径,来写一个工厂模式生成相应的类吧

工厂类

/**
 * @Author: ChenBin
 * @Date: 2018/5/2/0002 9:38
 */
public class Factory {

    public static Check Create(String className){
        try {
            return (Check) Class.forName(className).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

这个就不多说明了,就是反射一个实例化对象返回

我用的是SpringBoot加Mybatis,看下查询数据库的Mybatis中SQL

<select id="findByTools" resultType="com.uhope.data.export.domain.EdTools" parameterType="java.lang.String">
        SELECT
            id AS id,
            name AS name,
            type AS type,
            class_name AS className,
            create_time AS createTime,
            creator AS creator,
            description AS description,
            sort_order AS sortOrder
        FROM
            ed_tools
        WHERE
            id = (
                SELECT
                    tool_id
                FROM
                    ed_field_tools
                WHERE
                    field_id = (
                        SELECT
                            id
                        FROM
                            ed_template_field
                        WHERE
                            template_id = #{templateId}
                    )
            )
    </select>

用了两层子查询(没办法啊,关联表多着呢)来找到这个校验器在数据库中的信息

Mapper

/**
     * 多表联查工具表信息
     * @param templateId 模版ID
     * @return
     */
    List<EdTools> findByTools(String templateId);

Service接口我就不写了,直接看实现类

public HashMap<String, Object> export( Request request) {
        List<EdTools> tools = edTemplateMapper.findByTools(request.getTemplateId());
        Client client = new Client();

        for (EdTools tool : tools) {
            client.addCheck(Factory.Create(tool.getClassName()));
        }

        return client.validate(request, client);
    }

首先根据这个ID查询出当前模版选择了多少个校验器,创建场景实现类,根据查询出来的大小做个循环,我们要取出关键的className字段信息,循环的目的是,不管你选择了几个校验器,我就在这个循环里实例化几个校验器添加到addCheck里,自然就变成一个链子了,而对象哪里来?就根据库里的类路径通过工厂类反射来实例化,这样我们就不用关心其他校验器有没有被用到,我只给你想要的校验器,不管几个全都加到了List里了,会在Client这个类里就操作的,直接调用client.validate(request, client)就跑起来了,是成功还是失败都会返回的,这种方式也便于扩展,你需要别的校验器,实现Check接口就行,再实现对应的方法,其他代码无需修改了,if (index == checks.size())会来决定继续还是结束,根据模版选择的效验器数量循环里client.addCheck(Factory.Create(tool.getClassName()));不担心添加的问题,方便扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值