Java建造者模式案例实操:从理论到项目的完整指南
目录导读
- 建造者模式的核心概念与适用场景
- 经典案例:复杂对象的分步构建(含代码)
- Lombok @Builder 注解的实战应用
- 建造者模式与工厂模式的区别对比
- 常见陷阱与最佳实践(含问答)
- 何时该用建造者模式
建造者模式的核心概念与适用场景
建造者模式(Builder Pattern)属于创建型设计模式,它允许你分步骤创建复杂对象,将对象的构造过程与表示分离。

适用场景:
- 对象有多个构造参数(尤其超过4个)
- 参数存在可选或必填组合
- 对象构造需要分步骤执行(如组装汽车)
- 希望避免“伸缩构造函数”反模式
核心角色:
- Product(产品)——最终要创建的对象
- Builder(抽象建造者)——声明构造步骤
- ConcreteBuilder(具体建造者)——实现构造步骤
- Director(指挥者)——控制构造顺序(可选)
经典案例:复杂对象的分步构建
假设我们需要构建一个 Computer 对象,它有CPU、GPU、内存、硬盘、操作系统等属性,其中有些为必填,有些可选。
第一步:定义产品类
public class Computer {
// 必填参数
private String cpu;
private String gpu;
private int ram;
// 可选参数
private int storage;
private String os;
// 私有构造器,只允许通过Builder创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.gpu = builder.gpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.os = builder.os;
}
}
第二步:实现静态内部Builder类
public class Computer {
// ... 省略属性
public static class Builder {
// 必填参数
private String cpu;
private String gpu;
private int ram;
// 可选参数(设置默认值)
private int storage = 256;
private String os = "Windows 11";
// 必填参数的构造方法
public Builder(String cpu, String gpu, int ram) {
this.cpu = cpu;
this.gpu = gpu;
this.ram = ram;
}
// 为可选参数提供setter方法(链式调用)
public Builder storage(int val) {
storage = val;
return this;
}
public Builder os(String val) {
os = val;
return this;
}
// 构建最终对象
public Computer build() {
return new Computer(this);
}
}
}
第三步:客户端使用
Computer gamingPC = new Computer.Builder("i7-13700", "RTX 4080", 32)
.storage(1024)
.os("Windows 11 Pro")
.build();
Computer officePC = new Computer.Builder("i5-13400", "GTX 1660", 16)
.build(); // 仅必填参数
优势:
- 参数顺序无关——通过方法名明确参数含义
- 可选参数灵活——不必须设置所有字段
- 对象不可变——无setter方法,通过builder完成所有设置
Lombok @Builder 注解的实战应用
在Spring Boot项目中,更推荐使用Lombok注解简化开发。
示例:使用Lombok实现
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Long id;
private String username;
@Builder.Default
private String email = "default@example.com";
private int age;
}
使用方式:
User user = User.builder()
.username("张三")
.email("zhangsan@example.com")
.age(25)
.build();
User defaultUser = User.builder()
.username("李四")
.build(); // email取默认值
注意:Lombok的@Builder默认不会给你“必填参数校验”,需要添加@Builder(toBuilder=true)配合自定义校验注解。
建造者模式与工厂模式的区别对比
很多开发者容易混淆这两个模式,我们通过表格清晰比较:
| 对比维度 | 建造者模式 | 工厂模式 |
|---|---|---|
| 关注点 | 对象的构建过程 | 对象的创建类型 |
| 复杂度 | 适合复杂对象(多参数) | 适合简单对象或同类产品簇 |
| 灵活性 | 可控制构建顺序和步骤 | 返回完整对象,不可控步骤 |
| 典型应用 | 开发配置类、Request/Response对象 | 数据库连接池、日志记录器 |
| 代码特征 | 链式调用(.a().b().build()) | 单一方法(.create()) |
何时用建造者模式?
- 当你需要“分步骤构建”或“可选参数组合”
- 当你发现构造方法参数超过4个且部分可选
何时用工厂模式?
- 当你需要“根据类型创建不同子类”
- 当你希望封装对象创建逻辑(如单例约束)
常见陷阱与最佳实践(含问答)
Q1:Builder类应该放在Product内部还是外部?
回答:推荐放在Product内部作为静态内部类,理由:
- 访问私有构造函数更简单
- 与产品类紧密耦合,便于维护
- 防止外部类随意访问Builder
Q2:为什么需要build()方法做校验?
回答:build()方法虽然是最后一步,但可以拦截无效状态:
public Computer build() {
if (cpu == null || gpu == null) {
throw new IllegalStateException("CPU和GPU为必填项");
}
if (ram <= 0) {
throw new IllegalArgumentException("内存必须大于0");
}
return new Computer(this);
}
Q3:如何实现不可变对象?
回答:
- 将所有字段设置为
private final - 不提供setter方法
- Builder类方法返回
this实现链式调用 - 在构造器中做防御性拷贝(针对集合类型)
Q4:Lombok的@Builder有哪些坑?
回答:
- 不支持泛型Builder直接实例化(需要自定义Builder类型)
- @Builder.Default对集合类型默认值无效(需显式初始化如
ArrayList<>()) - 对于集成其他注解(如@AllArgsConstructor),要小心冲突
- 必填参数通过构造器传递,避免运行时遗漏
- 链式方法返回Builder自身,保持流畅性
- 在build()方法做参数校验,而不是分散在每个setter中
- Builder类应该保持final,防止继承破坏不变性
- 对于不可变对象,禁止在对象内部暴露Builder的引用
何时该用建造者模式
- ✅ 当对象构造参数超过3个(特别是可选参数混合)
- ✅ 当对象需要分步骤构建(如生成复杂报告)
- ✅ 当希望创建不可变对象(无setter、final字段)
- ✅ 当代码中反复出现“伸缩构造函数”(如
new X(a,b,c,d,e, null, null, f)) - ❌ 不要用于简单对象(参数少于3个且全必填)
- ❌ 不要与工厂模式混淆(工厂解决“类型”,建造者解决“构造过程”)
通过本文案例,你应该能够:
- 手写一个完整的Builder模式
- 在Spring Boot项目中熟练使用Lombok @Builder
- 区分建造者模式与工厂模式的适用场景
- 避免常见的设计陷阱
最后练习:尝试用Builder模式改造你项目中的某个DTO/配置类,你会立刻感受到代码可读性的提升。