1. 背景
系统中很多数据表只存储了数据的ID,没有存储其名字,但前端展示需要展示其名字。
比如
- 工单表只存储了创建人,更新人的ID,列表需要展示 创建人,更新人的中文名字。
- 订单表只存储了机构的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设置名字的处理步骤是相似的
- 遍历List获取Id,存储成List或Set。
- 根据多个Id查询一个Map<Id,Name>的数据
- 遍历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了