
本文介绍如何将重复的 stream + builder 模式代码提取为通用、清晰、易维护的方法,涵盖构造函数替代 builder、拆分流操作、使用方法引用优化过滤逻辑等 clean code 实践。
本文介绍如何将重复的 stream + builder 模式代码提取为通用、清晰、易维护的方法,涵盖构造函数替代 builder、拆分流操作、使用方法引用优化过滤逻辑等 clean code 实践。
在 Java 开发中,当多个业务逻辑块仅因构建器(Builder)调用的字段名不同(如 .similarParts() 与 .extendedSimilarParts())而重复时,极易产生冗余代码,违背 DRY 原则,也降低可读性与可测试性。下面以实际案例出发,逐步展示一套专业、可落地的重构方案。
✅ 第一步:用构造函数替代 Builder 调用
若 PartResultDto.Builder 长期仅设置固定字段(如 identifier 和某类 parts 列表),应优先在类中补充语义明确的构造函数:
public class PartResultDto {
private final String identifier;
private final List<partdto> similarParts;
private final List<partdto> extendedSimilarParts;
// 推荐:为常用组合提供构造函数(注意字段互斥性)
public PartResultDto(String identifier, List<partdto> similarParts) {
this(identifier, similarParts, null);
}
public PartResultDto(String identifier, List<partdto> extendedSimilarParts, boolean isExtended) {
this.identifier = identifier;
this.similarParts = isExtended ? null : extendedSimilarParts;
this.extendedSimilarParts = isExtended ? extendedSimilarParts : null;
}
// 或更清晰地——使用 builder 模式封装构造逻辑(见下文进阶建议)
}</partdto></partdto></partdto></partdto>
这样,原 Builder 链式调用可简化为:
new PartResultDto(s.getIdentifier(), results)
✅ 第二步:拆分流操作,提升可读性与可组合性
避免在 map 中嵌套多层逻辑(如检索 + 条件判断 + 构建)。应将职责分离:
- 第一个 map 负责获取数据;
- filter 独立处理空值校验;
- 第二个 map 专注对象构建。
重构后代码如下(以 similarParts 场景为例):
return search.stream()
.map(s -> retrieveSimilarParts(similarParts, s.getVdi4081()))
.filter(not(CollectionUtils::isNullOrEmpty))
.map(results -> new PartResultDto(s.getIdentifier(), results))
.toList(); // Java 16+,替代 Collectors.toList()
⚠️ 注意:s 在第二个 map 中已不可访问(因前一 map 输出为 List
return search.stream()
.flatMap(s -> {
List<partdto> results = retrieveSimilarParts(similarParts, s.getVdi4081());
return CollectionUtils.isNullOrEmpty(results)
? Stream.empty()
: Stream.of(new PartResultDto(s.getIdentifier(), results));
})
.toList();</partdto>
该写法天然规避了 null 过滤,语义更精准,且无需额外 filter 步骤。
✅ 第三步:抽象共用逻辑,支持扩展场景
针对 similarParts 与 extendedSimilarParts 的差异,可定义函数式接口或方法参数化:
private List<partresultdto> buildPartResults(
List<searchitem> search,
Function<searchitem list>> partRetriever) {
return search.stream()
.flatMap(s -> {
List<partdto> results = partRetriever.apply(s);
return CollectionUtils.isNullOrEmpty(results)
? Stream.empty()
: Stream.of(new PartResultDto(s.getIdentifier(), results));
})
.toList();
}
// 调用方式:
List<partresultdto> similar = buildPartResults(search, s -> retrieveSimilarParts(similarParts, s.getVdi4081()));
List<partresultdto> extended = buildPartResults(search, s -> retrieveSimilarParts(extendedSimilarParts, s.getVdi4081()));</partresultdto></partresultdto></partdto></searchitem></searchitem></partresultdto>
? 总结建议
- 优先构造函数而非 Builder:当字段组合稳定时,构造函数更轻量、类型安全、IDE 支持更好;
- 拒绝“胖 map”:每个 Stream 操作应只做一件事,复杂逻辑务必抽离为独立方法;
- 善用 flatMap 替代 map + filter + null 模式:更符合函数式思维,减少中间对象;
- 用函数式参数统一变体逻辑:比复制粘贴高明十倍,也为未来新增 legacyParts 等场景预留扩展点。
Clean Code 不是追求行数最少,而是让每行代码都不言自明、易于验证、难以出错。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











