Java基础

简化补充关联对象的业务代码

2019-11-25  本文已影响0人  十毛tenmao

业务项目中经常有跨表对象或者跨服务的对象,对象之间使用Id关联,但是返回到调用方时,又需要根据id补充完整的关联对象。这种模式非常常用,所以写了一个工具类,简化了这个步骤

场景描述

问题中有分类信息,但是默认保存在库表中的只有分类Id(categoryId),但是接口返回给调用方的时候,需要补充完整的Category信息

@Data
public class Question {
    private Integer id;
    /**
     * 分类ID.
     */
    private Integer categoryId;

    private Category category;

    /**
     * 问题.
     */
    private String question;
}
@Data
public class Category {
    private Integer id;
    private String name;
}

默认情况下,从数据库中查询问题列表时,只有categoryId,没有CategoryCategory需要根据id再进行远程调用获取(有的是可以通过联表查询)

SupplementUtil<Question, Integer, Category> supplement = SupplementUtil.<Question, Integer, Category>builder()
        .idProviderForTarget(Question::getCategoryId)
        .idProviderForObj(Category::getId)
        .objSetter(Question::setCategory)
        .build();
supplement.supplementWithConverter(questions, categoryManager::getByList);

使用这种方式可以减少很多胶水代码

SupplementUtil定义

/**
 * 对象补充器.
 * 通过Target中的ID,填充ID对应的对象Obj到Target
 *
 * @param <Target> 被补充的目标对象
 * @param <Id>     补充对象的ID
 * @param <Obj>    补充的对象
 * @author tenmao
 * @since 2019/11/13
 */
@Builder
public class SupplementUtil<Target, Id, Obj> {
    /**
     * Target中获取Obj的Id的方法.
     */
    @NonNull
    private Function<Target, Id> idProviderForTarget;

    /**
     * Obj中获取Id的方法.
     */
    @NonNull
    private Function<Obj, Id> idProviderForObj;

    /**
     * Target中设置Obj的方法.
     */
    @NonNull
    private BiConsumer<Target, Obj> objSetter;

    /**
     * 是否要求ID对应的Obj一定存在.
     */
    private boolean requireExists = true;

    /**
     * 如果ID赌赢的Obj不存在,则使用该默认值.
     */
    private Function<Id, Obj> defaultObj = null;

    /**
     * 使用ID到Obj的转换器来补充目标对象Targets
     *
     * @param targets   目标对象
     * @param converter ID到Obj的转换器
     */
    public void supplementWithConverter(List<Target> targets, Function<List<Id>, List<Obj>> converter) {
        //获取所有Id
        List<Id> ids = targets.stream().map(idProviderForTarget).filter(Objects::nonNull).collect(Collectors.toList());
        if (ids.isEmpty()) {
            return;
        }

        //获取Id列表对应的Obj列表,并转换为Map
        Map<Id, Obj> objMap = converter.apply(ids).stream().collect(Collectors.toMap(idProviderForObj, Function.identity()));

        supplementWithMap(targets, objMap);
    }

    /**
     * 使用ID到Obj的Map来补充目标对象Targets
     *
     * @param targets 目标对象
     * @param objMap  ID到Obj的Map
     */
    public void supplementWithMap(List<Target> targets, Map<Id, Obj> objMap) {
        batchSupplement(targets, idProviderForTarget, objSetter, objMap, requireExists, defaultObj);
    }

    /**
     * 批量根据ID扩充对象信息.
     *
     * @param targets       扩充对象的目标对象
     * @param idProvider    ID提供方法
     * @param objSetter     对象设置器
     * @param objMap        ID到对象的Map
     * @param requireExists 是否要求必须(如果是,但是没有存在,则会抛出异常{@link IllegalArgumentException})
     * @param defaultObj    如果没有匹配到则使用默认值
     * @param <Target>      扩充对象的目标对象
     * @param <Id>          ID
     * @param <Obj>         ID对应的对象
     */
    public static <Target, Id, Obj> void batchSupplement(List<Target> targets,
                                                         Function<Target, Id> idProvider,
                                                         BiConsumer<Target, Obj> objSetter,
                                                         Map<Id, Obj> objMap,
                                                         boolean requireExists,
                                                         Function<Id, Obj> defaultObj) {
        if (targets == null || idProvider == null || objSetter == null || objMap == null) {
            throw new IllegalArgumentException("parameter for supplement should not be empty");
        }
        for (Target target : targets) {
            Id id = idProvider.apply(target);
            if (id != null) {
                Obj o = objMap.get(id);
                if (o == null && requireExists) {
                    throw new IllegalArgumentException(String.format("id:%s cannot convert to object", id));
                }
                objSetter.accept(target, o != null ? o : defaultObj.apply(id));
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读