吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 924|回复: 6
收起左侧

[学习记录] 使用 Java 8 函数式编程减少重复代码:Function 应用

  [复制链接]
窝窝头4毛钱一个 发表于 2023-3-7 09:54
本帖最后由 窝窝头4毛钱一个 于 2023-3-7 09:57 编辑

问题背景

我们的项目中常常会对数据做一些校验,例如添加一个用户,我们需要校验这些内容:

用户名称是否已被使用
邮箱是否已被使用
工号是否已被使用
手机号是否已被使用

等等这些校验

例如添加用户方法

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(UserAddDTO dto) {
            // 校验名称是否重复
            throwExceptionWhenNameExists(dto);
            // 校验邮箱是否重复
            throwExceptionWhenEmailExists(dto);
            // 校验手机号是否重复
            throwExceptionWhenPhoneExists(dto);
            // 校验工号是否重复
            throwExceptionWhenWorkNoExists(dto);

            SysUser user = init(dto);
            // 保存用户
            save(user);
            // 保存用户角色
            saveRoles(dto, user);
            // 保存用部门
            saveDept(dto, user);
    }

校验名称是否重复

private void throwExceptionWhenNameExists(UserAddDTO dto) {
    LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(SysUser::getUsername, dto.getUsername());
    long count = count(wrapper);
    if(count > 0) throw new RuntimeException("用户名称已被使用");
}

校验邮箱是否重复

private void throwExceptionWhenEmailExists(UserAddDTO dto) {
    LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(SysUser::getEmail, dto.getEmail());
    long count = count(wrapper);
    if(count > 0) throw new RuntimeException("邮箱已被使用");
}

问题分析

上面的这种做法,对于每个字段,我们都要写一个方法。感觉非常的麻烦,而且一个项目有很多这样的地方。
如果每个地方都是这种写法,就会浪费比较多的时间。
我们可以发现,像是这种校验都有一个共同的特征,就是通过某个字段统计在表中的数量,我们是不是可以像 MyBatis-Plus 一样,将获取该字段的方法传递过去就行了?
对于要验证的字段,我们可以通过 SysUser::getUserName、SysUser::getPhone 这种方式传递过去,然后再将对应的值传过去。最终期望的结果是这样的:

// 校验名称是否重复
assertFieldNoRepeat(dto.getUserName(), SysUser::getUserName, "用户名称已被使用");

// 校验邮箱是否重复
assertFieldNoRepeat(dto.getUserName(), SysUser::getUserName, "邮箱已被使用");

//......

以终为始,以结果为导向反过来推导:
首先,我们将需要校验的值传进去
其次,再将这个值对应的字段传进去
然后,将异常消息给传进去
这样子,同一个类,无论那个字段,都可以用这种方式校验是否重复了
最终得出的方法是这样的

优化后的方法

/**
 * 重复字段校验
 * @Param getColumn 被验证的字段
 * @param val 被校验的值
 * @param msg 异常消息
 */
private void assertFieldNoRepeat(SFunction<SysUser, Object> getColumn, 
                                 Object val, 
                                 String msg) {
    LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(getColumn, val);
    long count = count(wrapper);
    if(count > 0) throw new RuntimeException(msg);
}

这样子,我们就不用为每一个字段都编写一个校验方法了。

再优化一下

除了 SysUser 之外,是不是其他的表也存在很多这样的场景。
我们是不是可以将这个方法再提取一下,变成一个工具类。
对于不同的类,我们可以通过泛型的方式传进来,对于 count 方法,我们是不是可以同样使用 Function 的方式传进来。我们最终期望的效果是这样的:

// SysUser 校验名称是否重复
FieldValidUtil.assertFieldNoRepeat(dto.getUsername(), SysUser::getUsername, this::count, "用户名称已被使用");

// SysUser 校验邮箱是否重复
FieldValidUtil.assertFieldNoRepeat(dto.getEmail(), SysUser::getEmail, this::count, "邮箱已被使用");

// 或者别的类
// Material 物料校验编码是否重复
FieldValidUtil.assertFieldNoRepeat(materialDto.getCode(), Material::getCode, materialService::count, "物料编码已被使用");

以终为始,通过反方向推导:
首先,我们需要将被校验的值传入,
其次,再将这个值对应的类、字段传进去,
然后,再通过这个类的 Service 的 count 方法统计数量
最终推导出的工具类的实现是这样子的:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

/**
 * 字段校验工具
 */
public class FieldValidUtil {

    /**
     * 重复字段校验
     * @param val 被校验的值
     * @param getColumn 被校验的列
     * @param method 指定的方法
     * @param msg 异常消息
     */
    public static <T> void assertFieldNoRepeat(Object val, SFunction<T, Object> getColumn, 
                                           SFunction<LambdaQueryWrapper<T>, Long> method, 
                                           String msg) {
        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(getColumn, val);
        Long count = method.apply(wrapper);
        if(count > 0) throw new RuntimeException(msg);
    }
}

总结

我们要善于发现工作中的这些痛点,然后分析这些问题的共同点,进行抽象归纳,找到共性,然后想办法将这些同类型的问题用同一种方式解决,避免将时间浪费在重复的事情上。



免费评分

参与人数 2吾爱币 +5 热心值 +2 收起 理由
zpolar + 1 + 1 昨天刚学到文档注释...
侃遍天下无二人 + 4 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

15593600257 发表于 2023-3-7 11:55
可以的很不错
pjy612 发表于 2023-3-7 15:24
感觉似乎还能再精简一下 把规则用 Func 放在 list 里面。循环 走规则 啥的。
qwerdf4567 发表于 2023-3-7 15:28
 楼主| 窝窝头4毛钱一个 发表于 2023-3-7 21:25
pjy612 发表于 2023-3-7 15:24
感觉似乎还能再精简一下 把规则用 Func 放在 list 里面。循环 走规则 啥的。

之前有想过变成一个 List 去保存这几个验证,但是还没有思路。或许你可以试试看,要是弄好了可以告诉我一下[手动旺柴]
mallorJava 发表于 2023-3-8 10:05
首先感觉名字还可以再优化一下,不是判断字段是否重复,是判断用户是否重复;其次字段直接遍历就行了,三四行代码就搞定了。
 楼主| 窝窝头4毛钱一个 发表于 2023-3-9 15:53
mallorJava 发表于 2023-3-8 10:05
首先感觉名字还可以再优化一下,不是判断字段是否重复,是判断用户是否重复;其次字段直接遍历就行了,三四 ...

哈哈,因为不只是 User 表使用,所以起了个抽象点的名字。你可以根据自己的喜好修改即可。
关于字段遍历的,你可以把代码贴出来看看。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 03:04

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表