Java 23 种设计模式:从踩坑到精通 | 建造者模式 —— 构造器参数太多?试试链式调用
Java 23 种设计模式:从踩坑到精通 | 建造者模式 —— 构造器参数太多?试试链式调用
摘要:当一个对象有十几个甚至几十个可选参数时,重叠构造器会让代码臃肿难读,
setter又可能破坏不可变性。建造者模式通过链式调用分步构建对象,既清晰又安全。本文从“构造器地狱”出发,带你拆解 Builder 的实现原理、变体写法,与工厂模式对比,并给出 Lombok@Builder、StringBuilder等实战案例,彻底掌握优雅的对象创建之道。
📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 上一篇:抽象工厂模式 | 当前:建造者模式 | 下一篇:原型模式
🔗 返回系列总目录
1. 从“构造器地狱”说起
假设你要定义一个“电脑”对象,它有 CPU、内存、硬盘、显卡、显示器等几十个配置,有些是必选的,大多数是可选的。如果使用重叠构造器,代码会变成这样:
public class Computer {
private String cpu; // 必选
private String ram; // 必选
private String hdd; // 可选
private String gpu; // 可选
private String monitor; // 可选
public Computer(String cpu, String ram) { ... }
public Computer(String cpu, String ram, String hdd) { ... }
public Computer(String cpu, String ram, String hdd, String gpu) { ... }
// 无穷无尽的构造器重载...
}
调用时参数顺序极易出错,可读性极差。如果改用 JavaBean 的 setter 模式,虽然调用清晰了,却会导致对象在构造过程中可能处于不一致状态,且失去了不可变性。
建造者模式正是为这类“参数爆炸”场景而生的:它通过一个内部 Builder 类,将必选参数放在 Builder 构造器中,可选参数通过链式方法设置,最后调用 build() 一次性创建不可变对象。
1.1 你的代码该不该用 Builder?
| 判断标准 | 是 → 用 Builder | 否 → 用普通构造器/工厂 |
|---|---|---|
| 对象参数 ≥ 4 个,且多数可选 | ✅ | ❌ |
| 需要保证对象创建后不可变 | ✅ | ❌ |
| 构造过程中需要参数校验 | ✅ | ❌ |
| 参数只有 2~3 个且全部必选 | ❌ | ✅ |
2. 模式定义与 UML 结构
建造者模式 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。它属于 创建型设计模式。
标准建造者模式的 UML 类图:

四个角色:
- 产品(Product):最终被创建的复杂对象;
- 抽象建造者(Builder):定义创建产品各个部件的接口;
- 具体建造者(ConcreteBuilder):实现 Builder 接口,完成各部件的具体建造并返回产品;
- 导演(Director):控制建造过程,按特定顺序调用建造者的构建步骤(可选)。
3. 代码实现:组装电脑
3.1 产品类
public class Computer {
private String cpu; // 必选
private String ram; // 必选
private String hdd; // 可选
private String gpu; // 可选
private String monitor; // 可选
// 构造器设为 private,只能通过 Builder 创建
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.hdd = builder.hdd;
this.gpu = builder.gpu;
this.monitor = builder.monitor;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", hdd='" + hdd + '\'' +
", gpu='" + gpu + '\'' +
", monitor='" + monitor + '\'' +
'}';
}
// 静态内部类建造者
public static class Builder {
private String cpu;
private String ram;
private String hdd;
private String gpu;
private String monitor;
// 必选参数通过 Builder 构造器传入
public Builder(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
// 可选参数通过链式 setter 设置
public Builder hdd(String hdd) {
this.hdd = hdd;
return this;
}
public Builder gpu(String gpu) {
this.gpu = gpu;
return this;
}
public Builder monitor(String monitor) {
this.monitor = monitor;
return this;
}
// 最终构建方法
public Computer build() {
return new Computer(this);
}
}
}
✅ 构建链式调用清晰易读,且
Computer对象一旦创建便不可变,线程安全。
3.2 客户端调用
Computer computer = new Computer.Builder("Intel i9", "32GB")
.hdd("1TB SSD")
.gpu("RTX 4090")
.monitor("4K Dell")
.build();
System.out.println(computer);
// Computer{cpu='Intel i9', ram='32GB', hdd='1TB SSD', gpu='RTX 4090', monitor='4K Dell'}
3.3 使用 Director 控制标准流程
当需要按照严格步骤建造,或者存在多种标准配置时,可以引入导演类。
public class ComputerDirector {
public Computer buildGamingPC() {
return new Computer.Builder("AMD Ryzen 9", "32GB")
.gpu("RTX 4080")
.hdd("2TB NVMe")
.monitor("144Hz 2K")
.build();
}
public Computer buildOfficePC() {
return new Computer.Builder("Intel i5", "16GB")
.hdd("512GB SSD")
.build();
}
}
4. 建造者模式 vs 工厂模式
| 对比维度 | 工厂模式 | 建造者模式 |
|---|---|---|
| 关注点 | 产品类型的选择(创建哪种产品) | 产品内部构件的组装过程(如何一步步创建) |
| 对象复杂度 | 一般创建单个对象,构造简单 | 创建复杂对象,参数多、构建步骤多 |
| 表示差异 | 不同工厂返回不同子类产品 | 同样的构建过程可以创建不同表示(内部组成不同) |
| 创建方式 | 一次性返回完整对象 | 分步骤构建,最后调用 build() 产出 |
💡 选型口诀:创建对象简单、重点在种类 → 用工厂;创建对象复杂、参数多、步骤多 → 用建造者。
5. 框架与实践中的应用
5.1 JDK:StringBuilder
StringBuilder 通过 append() 逐步添加内容,最后 toString() 返回不可变字符串。
String result = new StringBuilder()
.append("Hello")
.append(" ")
.append("World")
.append("!")
.toString();
5.2 Lombok @Builder
Lombok 在编译期自动生成建造者代码。
@Builder
public class User {
private String name;
private int age;
private String email;
}
User user = User.builder()
.name("Andy")
.age(30)
.email("andy@example.com")
.build();
5.3 Spring:RestTemplateBuilder / UriComponentsBuilder
Spring 中大量使用 Builder 来构建复杂对象。
5.4 MyBatis:SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 负责读取配置文件,一步步构建出 SqlSessionFactory,是标准的建造者模式。
6. 优缺点一览
| 优点 | 缺点 |
|---|---|
| 解耦构建与表示:客户端不必知道产品内部结构 | 代码量增加:每个产品都要配套建造者类 |
| 链式调用,可读性强 | 接口变动影响大:增加字段需要同步修改 Builder |
保证对象完整性:可在 build() 中校验 |
不适合参数很少的简单对象 |
7. 常见误区与面试高频题
❌ 误区1:建造者模式就是链式 setter
链式调用只是表现,核心在于分离构建与表示,并保证最终对象的不可变性。
❌ 误区2:参数多就一定用 Builder
如果参数只有两三个且都是必选,简单构造器或静态工厂方法更简洁。
💡 面试高频追问
- Builder 与工厂模式的区别? → 工厂关注产品类型,一次性创建;Builder 关注内部组装,分步构建。
- StringBuilder 为什么是建造者模式? →
append()逐步添加字符,最终toString()返回不可变字符串。 - Lombok @Builder 的优缺点? → 简化代码,但隐藏细节,且不便于调试。
🎉 恭喜:如果你能分清“参数多且可选用 Builder,参数少且固定用工厂/构造器”,你就已经掌握了创建型模式选型的核心原则。面试中问到“创建复杂对象的最佳实践”,Builder 就是你的王牌答案。
8. 六大设计原则在建造者模式中的体现
| 设计原则 | 体现 |
|---|---|
| 单一职责(SRP) | 产品负责数据,Builder 负责组装,Director 负责流程 |
| 开闭原则(OCP) | 可通过扩展具体 Builder 创建不同产品表示 |
| 里氏替换(LSP) | 不同 Builder 遵循同一接口,可替换 |
| 依赖倒置(DIP) | Director 依赖抽象 Builder,不依赖具体建造者 |
| 接口隔离(ISP) | Builder 接口只定义构建相关方法 |
| 迪米特法则(LoD) | 客户端只与 Builder 交互,无需了解产品内部 |
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 上一篇:抽象工厂模式
- 当前:建造者模式(你在这里)
- 下一篇:原型模式
- 创建型模式汇总:单例、工厂、建造者、原型
- 结构型模式汇总:适配器、装饰器、代理……
- 行为型模式汇总:观察者、策略、模板方法……
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀 下一篇:原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?🚧 即将发布,敬请关注!
📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。
更多推荐

所有评论(0)