简化数据校验:Java 8 函数式编程的实用指南

344 阅读2分钟

背景

在 Java 开发中,重复代码常常让项目难以维护,增加了负担。Java 8 引入了函数式编程,尤其是 Function 接口,它提供了高效处理重复逻辑的工具。

本文展示了如何用 Java 8 的函数式特性来简化数据有效性校验,(可把有共性的检查抽离出来),减少代码重复。

场景示例:数据校验的困境

在复杂的业务系统中,常需要检查数据库字段值的有效性。

例如,验证用户 ID 和部门 ID 的存在性,传统做法需要手动编写大量查询代码,不仅冗长且维护困难:

public void checkUserExistence(String userId) {
    User user = userDao.findById(userId);
    if (user == null) {
        throw new RuntimeException("用户ID无效");
    }
}

public void checkDeptExistence(String deptId) {
    Dept dept = deptDao.findById(deptId);
    if (dept == null) {
        throw new RuntimeException("部门ID无效");
    }
}

使用函数式接口优化代码

Java 8 引入了 Function<T, R> 接口,允许将属性提取和查询逻辑封装成 Lambda 表达式,减少重复代码。SFunction 是一个对 Lambda 表达式的封装,使操作更简洁。

通用方法示例

以下方法 ensureColumnValueValid 实现了针对任意字段的通用校验:

public static <T, R, V> void ensureColumnValueValid(
    V valueToCheck, SFunction<T, R> columnExtractor,
    SFunction<LambdaQueryWrapper<T>, T> queryExecutor,
    String errorMessage) {

    if (valueToCheck == null) return;

    LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(columnExtractor);
    wrapper.eq(columnExtractor, valueToCheck);
    wrapper.last("LIMIT 1");

    T entity = queryExecutor.apply(wrapper);
    if (entity == null || columnExtractor.apply(entity) == null) {
        throw new DataValidationException(String.format(errorMessage, valueToCheck));
    }
}

应用示例

public void assignTaskToUser(AddOrderDTO dto) {
    ensureColumnValueValid(dto.getUserId(), User::getId, userDao::getOne, "用户ID无效");
    ensureColumnValueValid(dto.getDeptId(), Dept::getId, deptDao::getOne, "部门ID无效");
}

这种方式减少了代码量,提高了代码的可读性和维护性。

拓展:高级校验逻辑

校验列值是否符合预期

validateColumnValueMatchesExpected 方法验证列值是否与预期值匹配:

public static <T, R, C> void validateColumnValueMatchesExpected(
    SFunction<T, R> targetColumn, R expectedValue,
    SFunction<T, C> conditionColumn, C conditionValue,
    SFunction<LambdaQueryWrapper<T>, T> queryMethod,
    String errorMessage) {

    LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(targetColumn).eq(conditionColumn, conditionValue);

    T one = queryMethod.apply(wrapper);
    if (one != null && !Objects.equals(targetColumn.apply(one), expectedValue)) {
        throw new RuntimeException(String.format(errorMessage, expectedValue));
    }
}

校验值是否在预期值列表内

validateColumnValueInExpectedList 方法用于检查列值是否在一组期望值中:

public static <T, R, C> void validateColumnValueInExpectedList(
    SFunction<T, R> targetColumn, List<R> expectedValueList,
    SFunction<T, C> conditionColumn, C conditionValue,
    SFunction<LambdaQueryWrapper<T>, T> queryMethod,
    String errorMessage) {

    LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(targetColumn).eq(conditionColumn, conditionValue);

    T one = queryMethod.apply(wrapper);
    if (one != null && !expectedValueList.contains(targetColumn.apply(one))) {
        throw new RuntimeException(errorMessage);
    }
}

优势总结

  • 减少重复代码:复用 ensureColumnValueValid 等方法,避免大量重复校验逻辑。
  • 增强代码复用:通用校验方法适用于各种场景,能对不同实体的属性校验一体化处理。
  • 提高可读性和维护性:函数式编程使代码结构清晰,减少了后续维护难度。
  • 灵活性和扩展性:修改校验规则只需调整核心方法,所有调用处自动适配新规则。

总结

Java 8 的函数式编程极大地简化了代码逻辑,通过 Function 接口和 Lambda 表达式的组合,我们可以创建更简洁、灵活、易维护的代码。函数式编程思想值得在日常开发中深入探索,提升代码的优雅性和效率。