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了

相关推荐
考虑考虑6 分钟前
时间转换格式出现错误
java·后端·java ee
乘风破浪酱5243610 分钟前
实战排查:如何从Nginx配置中顺藤摸瓜找到Java应用的真实端口与日志位置
后端
꧁༺摩༒西༻꧂17 分钟前
Flask
后端·python·flask
爱分享的鱼鱼18 分钟前
为什么使用express框架
前端·后端
程序员清风19 分钟前
字节三面:微博大V发博客场景,使用推模式还是拉模式?
java·后端·面试
笨蛋不要掉眼泪1 小时前
SpringBoot项目Excel模板下载功能详解
java·spring boot·后端·spring·excel·ruoyi
程序员蜗牛1 小时前
你写代码会复用公共SQL么?
后端
猿究院-陆昱泽1 小时前
Redis 主从同步:原理、配置与实战优化
redis·后端·java-ee·intellij-idea
老葱头蒸鸡1 小时前
(23)ASP.NET Core2.2 EF关系数据库建模
后端·asp.net
啦工作呢2 小时前
Sass:CSS 预处理器
开发语言·后端·rust