You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Builder模式下相似代码的清洁方法提取咨询

提取相似代码为可复用方法的方案

问题场景

两段代码逻辑高度一致,仅存在两处差异:

  1. 调用retrieveSimilarParts时传入的第一个参数不同(extendedSimilarParts / similarParts)
  2. 构建PartResultDto时调用的Builder方法不同(.extendedSimilarParts(results) / .similarParts(results))

原代码片段:

第一段代码

return search.stream().map(s -> {
            List<PartDto> results = retrieveSimilarParts(extendedSimilarParts, s.getVdi4081());

            return CollectionUtils.isNullOrEmpty(results) ? null : PartResultDto.builder()
                    .identifier(s.getIdentifier())
                    .extendedSimilarParts(results).build();
        }).filter(Objects::nonNull).collect(Collectors.toList());

第二段代码

return search.stream().map(s -> {
                List<PartDto> results = retrieveSimilarParts(similarParts, s.getVdi4081());

                return CollectionUtils.isNullOrEmpty(results) ? null : PartResultDto.builder()
                        .identifier(s.getIdentifier())
                        .similarParts(results).build();
            }).filter(Objects::nonNull).collect(Collectors.toList());

解决方案

可以通过封装通用逻辑+传入差异点的方式实现代码复用,以下是几种简洁的实现方式:

方式一:使用BiConsumer传递Builder操作

定义一个通用方法,把检索参数和Builder的设置动作作为参数传入,核心逻辑只写一次:

private List<PartResultDto> processSimilarParts(List<SearchItem> search, 
                                                String retrievalParam,
                                                BiConsumer<PartResultDto.Builder, List<PartDto>> partsSetter) {
    return search.stream().map(s -> {
        List<PartDto> results = retrieveSimilarParts(retrievalParam, s.getVdi4081());
        if (CollectionUtils.isNullOrEmpty(results)) {
            return null;
        }
        PartResultDto.Builder builder = PartResultDto.builder()
                .identifier(s.getIdentifier());
        partsSetter.accept(builder, results);
        return builder.build();
    }).filter(Objects::nonNull).collect(Collectors.toList());
}

调用时只需传入对应差异参数即可:

  • 对应原第一段代码的调用:
processSimilarParts(search, extendedSimilarParts, (builder, results) -> builder.extendedSimilarParts(results));
  • 对应原第二段代码的调用:
processSimilarParts(search, similarParts, (builder, results) -> builder.similarParts(results));

方式二:自定义函数式接口(语义更清晰)

如果觉得BiConsumer的语义不够明确,可以自定义一个函数式接口来描述Builder的设置动作:

@FunctionalInterface
interface PartsBuilderSetter {
    void setParts(PartResultDto.Builder builder, List<PartDto> parts);
}

通用方法调整为使用自定义接口:

private List<PartResultDto> processSimilarParts(List<SearchItem> search, 
                                                String retrievalParam,
                                                PartsBuilderSetter partsSetter) {
    return search.stream().map(s -> {
        List<PartDto> results = retrieveSimilarParts(retrievalParam, s.getVdi4081());
        if (CollectionUtils.isNullOrEmpty(results)) {
            return null;
        }
        PartResultDto.Builder builder = PartResultDto.builder()
                .identifier(s.getIdentifier());
        partsSetter.setParts(builder, results);
        return builder.build();
    }).filter(Objects::nonNull).collect(Collectors.toList());
}

调用方式和之前一致,只是接口名称更具语义:

// 对应原第一段代码
processSimilarParts(search, extendedSimilarParts, (builder, results) -> builder.extendedSimilarParts(results));

// 对应原第二段代码
processSimilarParts(search, similarParts, (builder, results) -> builder.similarParts(results));

方式三:拆分细粒度逻辑(可选)

如果希望代码分层更清晰,可以把map中的转换逻辑单独提取为一个方法:

private PartResultDto mapToPartResult(SearchItem s, String retrievalParam, BiConsumer<PartResultDto.Builder, List<PartDto>> partsSetter) {
    List<PartDto> results = retrieveSimilarParts(retrievalParam, s.getVdi4081());
    if (CollectionUtils.isNullOrEmpty(results)) {
        return null;
    }
    PartResultDto.Builder builder = PartResultDto.builder().identifier(s.getIdentifier());
    partsSetter.accept(builder, results);
    return builder.build();
}

// 通用方法简化为:
private List<PartResultDto> processSimilarParts(List<SearchItem> search, 
                                                String retrievalParam,
                                                BiConsumer<PartResultDto.Builder, List<PartDto>> partsSetter) {
    return search.stream()
            .map(s -> mapToPartResult(s, retrievalParam, partsSetter))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
}

这样代码结构更松散,后续修改单一部分逻辑时更方便。

内容的提问来源于stack exchange,提问作者Thomas124124

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:05:39