java - 如何实现通用的对象填充方法

查看:567
本文介绍了java - 如何实现通用的对象填充方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问 题

先上代码:

User对象

public class UserDTO {
    private int userId;
    
    private String userName;
    
    private int orgId;
    
    private OrgDTO orgDTO;
}

Org对象

public Class OrgDTO {
    private int orgId;
    
    private String orgName;
}

工具类方法

public static <S, K, T> void oneToOne(List<S> sourceList, List<T> detailList, SafeFunction<S, K> sourceKey, SafeFunction<T, K> detailKey) {
    Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKey, Function.identity()));
    for (S s : sourceList) {
        K key = sourceKey.apply(s);
        T detail = detailMap.get(key);
        if (detail != null) {
            // TODO How to call s.setDetail() ?
        }
    }
}

参数说明:

  • sourceList: 待填充数组

  • detailList: 填充物数组

  • sourceKey: sourceList与detailList的关联键

  • detailKey: detailList的关联键转换函数

好吧,以上参数说明比较难懂(我也不知道要怎么描述)……

举个场景例子:
有一个UserDTOOrgDTO,假设他们在数据表的关系是1对1,很多时候我们需要根据UserDTO里面的orgId查询对应的OrgDTO,然后根据orgIdOrgDTO填充进UserDTO里面。

传统的代码写法如下:

public void fillOrgDTO(List<UserDTO> userDTOs) {
    Set<Integer> orgIds = userDTOs.stream().map(UserDTO::getOrgId).collect(Collectors.toSet());
    List<OrgDTO> orgDTOs = orgDAO.findByIds(orgIds);
    Map<Integer, OrgDTO> orgId2OrgDTOMap = orgDTOs.stream().collect(Collectors.toMap(OrgDTO::getOrgId, Function.identity()));
    for (UserDTO userDTO : userDTOs) {
        Integer orgId = userDTO.getOrgId();
        OrgDTO orgDTO = orgId2OrgDTOMap.get(orgId);
        if (orgDTO != null) {
            userDTO.setOrgDTO(orgDTO);
        }
    }
}

我的想法是,有没有办法把以上这段代码抽象成一个通用的方法,这样就不用每次都写类似的代码。

然后就试着写出了oneToOne这个方法,不过去到TODO那里就不知道怎么写了。

理论上用反射可以做到(需要变更方法签名),但我不想用反射,因为不论从性能还是维护角度来看,反射都不是一个很好的选择。

之前也了解过Java8的ConsumerBiConsumer,但试了一下好像也没法实现我的需求,可能是我理解得不够。

所以,在此提出这个问题,看看大家有没有好的解决方案?

PS:方法签名可以变更,代码可任意修改,UserDTO、OrgDTO可以实现任意接口。

解决方案

首先建议尽量用JDK functional interfaces,如果可以。下面是代码:

public <S, T, K> void oneToOne(List<S> sourceList, List<T> detailList, Function<S, K> sourceKeyExtractor, Function<T, K> detailKeyExactor,
        BiConsumer<S, T> action) {
    // Make sure the keys in detail list are unique.
    Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKeyExactor, Function.identity()));
    sourceList.stream().forEach(s -> action.accept(s, detailMap.getOrDefault(sourceKeyExtractor.apply(s), null)));
}

@Test
public void test_oneToOne() {
    List<UserDTO> userList = null; // TODO
    List<OrgDTO> orgList = null; // TODO
    oneToOne(userList, orgList, s -> s.getOrgId(), t -> t.getOrgId(), (s, t) -> s.setOrgDTO(t));
}

其次,是一个设计的问题:因为 sourceKeyExtractor, detailKeyExactor离它操作的对象源比较远,可能会混淆或至少要记得一个对应关系,如果重构一下,把sourceKeyExtractor移到前面一点,这样就一清二楚每个keyExtractor操作的对象源。

public <S, T, K> void oneToOne(List<S> sourceList, Function<S, K> sourceKeyExtractor, List<T> detailList, Function<T, K> detailKeyExactor,
        BiConsumer<S, T> action) {
    // Make sure the keys in detail list are unique.
    Map<K, T> detailMap = detailList.stream().collect(Collectors.toMap(detailKeyExactor, Function.identity()));
    sourceList.stream().forEach(s -> action.accept(s, detailMap.getOrDefault(sourceKeyExtractor.apply(s), null)));
}

这篇关于java - 如何实现通用的对象填充方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆