java如何使用函数式编程优雅处理根据ID设置Name

1. 背景

系统中很多数据表只存储了数据的ID,没有存储其名字,但前端展示需要展示其名字。

比如

  1. 工单表只存储了创建人,更新人的ID,列表需要展示 创建人,更新人的中文名字。
  2. 订单表只存储了机构的ID,产品的编码,列表需要展示机构名字,产品的名字。

2. 常规代码

java 复制代码
// 设置工单创建人名字
{
    List<WorkOrderQueryResp> workOrderList = new ArrayList();
    Set<Long> getCreatorIdSet = workOrderList.stream().map(WorkOrderQueryResp::getCreatorId).collect(Collectors.toSet());
    if (CollectionUtils.isNotEmpty(getCreatorIdSet)) {
        Map<Long, String> createNameMap = userService.queryNameByUserIds(getCreatorIdSet);
        for (WorkOrderQueryResp orderDTO : workOrderList) {
            orderDTO.setCreatorName(createNameMap.get(orderDTO.getCreatorId()));
        }
    }
}
// 设置呼叫数据创建人名字
{
    List<CallPageResp> pageResps = new ArrayList();
    Set<Long> getCreatorIdSet = pageResps.stream().map(CallPageResp::getCreatorId).collect(Collectors.toSet());
    if (CollectionUtils.isNotEmpty(getCreatorIdSet)) {
        Map<Long, String> createNameMap = userService.queryNameByUserIds(getCreatorIdSet);
        for (CallPageResp callPageResp : pageResps) {
            callPageResp.setCreatorName(createNameMap.get(callPageResp.getCreatorId()));
        }
    }

我发现 这类根据ID设置名字的处理步骤是相似的

  1. 遍历List获取Id,存储成List或Set。
  2. 根据多个Id查询一个Map<Id,Name>的数据
  3. 遍历List,从Map<Id,Name> 获取Id对应名字设置

很多时候只是处理的对象类型不同

3. 使用函数式编程对这类行为进行抽象

typescript 复制代码
/**
 * 
 * @param data 数据
 * @param nameSet 用于设置名称的函数(给对象设置名字)
 * @param idGet idGet 用于获取 ID 的函数(从对象中提取 ID)
 * @param getNameByIdsFunction 用于批量查询名称的函数(根据 ID 集合获取 ID - 名称映射)
 * @param <T>
 * @param <KEY>
 */
public static <T, KEY> void setName(List<T> data, BiConsumer<T, String> nameSet, Function<T, KEY> idGet,
                                    Function<Set<KEY>, Map<KEY, String>> getNameByIdsFunction) {
    if (CollectionUtils.isEmpty(data)) {
        return;
    }
    // 1 遍历List<Data>获取ID
    Set<KEY> filterIds = data.stream().map(idGet::apply).filter(Objects::nonNull).collect(Collectors.toSet());
    if (CollectionUtils.isEmpty(filterIds)) {
        return;
    }
    if (CollectionUtils.isNotEmpty(filterIds)) {
       // 2. 根据多个Id查询一个Map<Id,Name>的数据
        Map<KEY, String> idNameMap = getNameByIdsFunction.apply(filterIds);
        if (idNameMap == null) {
            idNameMap = Collections.emptyMap();
        }
       // 3. 遍历List<Data>,从Map<Id,Name> 获取Id对应名字设置
        data.forEach(e -> nameSet.accept(e, idNameMap.get(idGet.apply(e))));
    }
}

使用函数式编程简化后的代码如下

java 复制代码
// 设置工单创建人名字
{
    ListSetNameByIdUtil.setName(workOrderList,WorkOrderQueryResp::setCreatorName,WorkOrderQueryResp::getCreatorId,
    userService::queryNameByUserIds)
}
// 设置呼叫数据创建人名字
{
    ListSetNameByIdUtil.ssetName(pageResps,CallPageResp::setCreatorName,CallPageResp::getCreatorId,
    userService::queryNameByUserIds)
}

使用函数式编程抽象后,只需要一行代码就可以完成根据ID设置名字的操作,可以兼容多种返回对象类型。

可以向上再封装一层,写在UserService,后续有这类需要需求只需要写一行代码。

kotlin 复制代码
public <T> void setUserName(List<T> data, BiConsumer<T, String> nameSet, Function<T, Long> idGet) {
    ListSetNameByIdUtil.setName(data, nameSet, idGet, this::queryNameByUserIds);
}

总结

函数式编程用于封装共同的行为太nice了

相关推荐
xuxie1314 小时前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
重生成为编程大王15 小时前
Java中的多态有什么用?
java·后端
Funcy16 小时前
XxlJob 源码分析03:执行器启动流程
后端
豌豆花下猫17 小时前
Python 潮流周刊#118:Python 异步为何不够流行?(摘要)
后端·python·ai
秋难降18 小时前
SQL 索引突然 “罢工”?快来看看为什么
数据库·后端·sql
Access开发易登软件19 小时前
Access开发导出PDF的N种姿势,你get了吗?
后端·低代码·pdf·excel·vba·access·access开发
中国胖子风清扬19 小时前
Rust 序列化技术全解析:从基础到实战
开发语言·c++·spring boot·vscode·后端·中间件·rust
bobz96520 小时前
分析 docker.service 和 docker.socket 这两个服务各自的作用
后端
野犬寒鸦20 小时前
力扣hot100:旋转图像(48)(详细图解以及核心思路剖析)
java·数据结构·后端·算法·leetcode
phiilo20 小时前
golang 设置进程退出时kill所有子进程
后端