建造者模式
建造者模式用于分步骤构建复杂对象,把对象的创建过程和对象本身分开。它适合构造参数多、可选字段多、校验规则多、对象希望不可变的场景。
常见场景:HTTP 请求对象、导出任务配置、报表查询条件、消息发送命令、复杂 DTO、Lombok @Builder、MyBatis/Spring 中大量配置类。
为什么需要建造者
假设要创建一个导出任务:
java
ExportTask task = new ExportTask(
"asset",
"资产台账",
"xlsx",
true,
true,
10000,
LocalDateTime.now(),
"admin"
);这种长构造器有几个问题:
| 问题 | 后果 |
|---|---|
| 参数太多 | 调用方不知道每个位置代表什么 |
| 多个同类型参数 | 字符串、布尔值容易传反 |
| 可选参数多 | 构造器重载爆炸 |
| 校验分散 | 对象可能处于非法状态 |
| 创建过程不表达业务含义 | 读代码时难理解 |
建造者用链式方法表达字段含义,并在 build 时统一校验。
总体流程
mermaid
flowchart TD
A["创建 Builder"] --> B["设置必要字段"]
B --> C["设置可选字段"]
C --> D["build 统一校验"]
D --> E["创建不可变对象"]Java Demo:导出任务建造者
目标对象:
java
public class ExportTask {
private final String bizType;
private final String fileName;
private final String fileType;
private final boolean includeHeader;
private final int maxRows;
private final String operator;
private ExportTask(Builder builder) {
this.bizType = builder.bizType;
this.fileName = builder.fileName;
this.fileType = builder.fileType;
this.includeHeader = builder.includeHeader;
this.maxRows = builder.maxRows;
this.operator = builder.operator;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String bizType;
private String fileName;
private String fileType = "xlsx";
private boolean includeHeader = true;
private int maxRows = 10000;
private String operator;
public Builder bizType(String bizType) {
this.bizType = bizType;
return this;
}
public Builder fileName(String fileName) {
this.fileName = fileName;
return this;
}
public Builder fileType(String fileType) {
this.fileType = fileType;
return this;
}
public Builder includeHeader(boolean includeHeader) {
this.includeHeader = includeHeader;
return this;
}
public Builder maxRows(int maxRows) {
this.maxRows = maxRows;
return this;
}
public Builder operator(String operator) {
this.operator = operator;
return this;
}
public ExportTask build() {
if (bizType == null || bizType.isBlank()) {
throw new IllegalArgumentException("业务类型不能为空");
}
if (fileName == null || fileName.isBlank()) {
throw new IllegalArgumentException("文件名不能为空");
}
if (maxRows <= 0 || maxRows > 100000) {
throw new IllegalArgumentException("导出行数不合法");
}
return new ExportTask(this);
}
}
}调用:
java
ExportTask task = ExportTask.builder()
.bizType("asset")
.fileName("资产台账")
.fileType("xlsx")
.includeHeader(true)
.maxRows(50000)
.operator("admin")
.build();读起来比长构造器清楚很多。
建造者解决的不是 new,而是复杂构建过程
如果对象只有两三个字段:
java
new User(id, name);没必要上建造者。
建造者适合:
- 参数多。
- 可选字段多。
- 默认值多。
- 构建前要校验。
- 希望对象不可变。
- 构建步骤要表达业务含义。
和工厂模式区别
| 对比 | 建造者模式 | 工厂模式 |
|---|---|---|
| 关注点 | 一个复杂对象如何一步步构建 | 根据条件创建哪个对象 |
| 结果 | 通常是一类对象 | 可能是多个实现类之一 |
| 适合 | 参数多、构建过程复杂 | 创建逻辑变化、隐藏具体实现 |
| 例子 | 构建导出任务配置 | 根据支付类型获取支付服务 |
工厂解决“创建哪个”,建造者解决“怎么构建完整对象”。
Lombok @Builder
很多项目用 Lombok:
java
@Builder
public class ExportTask {
private String bizType;
private String fileName;
private String fileType;
}调用:
java
ExportTask task = ExportTask.builder()
.bizType("asset")
.fileName("资产台账")
.fileType("xlsx")
.build();注意:Lombok 只是生成 Builder 代码,不自动保证业务合法性。必要校验仍然要放在构造方法、静态工厂、build 扩展或 Service 中。
商业场景
医疗资产导出任务:
- 导出业务类型。
- 查询条件。
- 文件格式。
- 最大行数。
- 是否脱敏。
- 操作人。
- 回调地址。
这些字段既多又有默认值和校验,适合建造者。这样可以避免到处传 Map<String, Object>,也避免长构造器。
常见坑
| 问题 | 后果 | 建议 |
|---|---|---|
| 简单对象也用 Builder | 代码变长 | 字段少时直接构造 |
| Builder 不校验 | 构造出非法对象 | build 时统一校验 |
| 滥用 Map 代替 Builder | 类型不安全 | 明确定义字段 |
| Builder 对象复用 | 上一次字段污染下一次 | Builder 用完即丢 |
| Lombok 后忽略不可变性 | 对象仍可被随意改 | 配合 final、私有构造、无 setter |
面试标准回答
text
建造者模式用于分步骤构建复杂对象,适合参数多、可选字段多、默认值多、需要统一校验或不可变对象的场景。它能避免长构造器和构造器重载爆炸,让创建过程更清晰。和工厂模式相比,工厂关注创建哪个对象,建造者关注一个复杂对象如何构建。简单对象不需要 Builder,过度使用会增加代码量。本章小结
建造者模式的核心是“让复杂对象创建过程可读、可控、可校验”。它不是为了让 new 看起来高级,而是当构造过程真的复杂时,把参数、默认值、校验和不可变性集中管理。
