设计模式:不再手动 set DTO,采用 Builder 模式

37 阅读2分钟

一、背景

在实际项目中,我们经常需要构造一些字段很多的 DTO、请求对象或结果对象。 一开始,最自然的写法,往往就是 new 一个对象,然后一行一行 set

但当对象逐渐变复杂,这种写法会很快暴露问题。

这篇文章通过一个非常典型的对比,讲清楚: 为什么在复杂对象构建场景下,Builder 模式会比手动 set 更合适。

二、手动set

你一定见过这样的代码:

MatchResult result = new MatchResult();
result.setResumeId(resumeId);
result.setPositionId(positionId);
result.setFinalScore(finalScore);
result.setRagScore(ragScore);
result.setGraphScore(graphScore);
result.setLlmScore(llmScore);
result.setMatchedSkills(matchedSkills);
result.setMissingSkills(missingSkills);
result.setExtraSkills(extraSkills);
result.setRecommendLevel(recommendLevel);
result.setMatchGrade(matchGrade);
123456789101112

手动 set 写法的几个问题

  • 代码冗余,可读性差

  • 容易构造出“半成品对象”

  • 必填字段只能靠约定

  • 扩展成本高,容易漏改

三、使用builder模式

@Data

@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MatchResult {

    
    private String resumeId;

    
    private String positionId;

    
    private float finalScore;

    
    private float ragScore;

    
    private float graphScore;

    
    private float llmScore;

    
    private List matchedSkills;

    
    private List missingSkills;

    
    private List extraSkills;

    
    private String llmReport;

    
    private Map scoreDetails;

    
    private int recommendLevel;

    
    private String matchGrade;
}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
MatchResult result = MatchResult.builder()
        .resumeId(resumeId)
        .positionId(positionId)
        .finalScore(finalScore)
        .ragScore(ragScore)
        .graphScore(graphScore)
        .llmScore(llmScore)
        .matchedSkills(matchedSkills)
        .missingSkills(missingSkills)
        .extraSkills(extraSkills)
        .recommendLevel(recommendLevel)
        .matchGrade(matchGrade)
        .build();
12345678910111213

四、使用builder模式的好处

1、 可读性明显更好

builder()
  .xxx()
  .yyy()
  .zzz()
  .build()
12345

2、对象构建是原子操作

MatchResult result = MatchResult.builder()
        ...
        .build();

要么构建成功, 要么直接失败。

不会再出现“半成品对象”。

3、对扩展更加友好 当新增字段时:

Builder 增加一个方法

旧代码不需要改

需要使用新字段的地方再补 不需要更改之前的原始代码