Appearance
建造者模式 (Builder Pattern)
一、定义
一句话概括:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
官方定义(GoF):Separate the construction of a complex object from its representation so that the same construction process can create different representations.
建造者模式的核心思想是:分步骤构建复杂对象。当对象有多个可选参数、需要复杂的初始化过程时,建造者模式可以将对象的"组装过程"与"最终表示"解耦。
二、解决的问题
2.1 什么场景下需要建造者模式?
- 构造参数过多:一个类有十几个甚至几十个参数,构造器参数列表过长
- 参数可选:很多参数是可选的,用 telescoping constructor(重叠构造器)方式会导致构造器爆炸
- 对象构造复杂:创建对象需要多个步骤,且步骤之间有依赖关系
- 不可变对象:需要创建不可变对象,且参数较多
- 需要不同的表示:同样的构建过程可以产生不同的产品
2.2 不用建造者会有什么问题?
java
// 问题1:重叠构造器(Telescoping Constructor)—— 参数爆炸
public class Computer {
private String cpu;
private String ram;
private String storage;
private String gpu;
private String motherboard;
private String powerSupply;
private String caseType;
private boolean hasWifi;
private boolean hasBluetooth;
private boolean hasRGB;
// 如果每个参数都可选,需要多少个构造器?2^10 = 1024 个!
public Computer(String cpu, String ram) { ... }
public Computer(String cpu, String ram, String storage) { ... }
public Computer(String cpu, String ram, String storage, String gpu) { ... }
// 噩梦...
// 问题2:setter 方式 —— 无法保证不可变性
// 对象创建后可以被修改,线程不安全
Computer c = new Computer();
c.setCpu("i7"); // 可能在 set 过程中被其他线程读取到不完整状态
c.setRam("16GB");
// ...
}三、结构
3.1 文字描述
建造者模式包含四个角色:
- Product(产品):要创建的复杂对象
- Builder(抽象建造者):定义构建步骤的接口
- ConcreteBuilder(具体建造者):实现构建步骤,提供获取产品的方法
- Director(指挥者):控制构建过程,按照特定顺序调用建造者的方法
- Client(客户端):使用指挥者或直接使用建造者创建产品
3.2 ASCII 类图
┌──────────────────┐ ┌──────────────────────┐
│ Director │ │ <<interface>> │
├──────────────────┤ │ Builder │
│ - builder: Builder│───────>├──────────────────────┤
│ + construct() │ │ + buildPartA(): void │
│ : Product │ │ + buildPartB(): void │
└──────────────────┘ │ + buildPartC(): void │
│ + getResult(): Product │
└──────────────────────┘
△
│
┌──────────┴──────────┐
│ │
┌──────────────┐ ┌──────────────┐
│ConcreteBuilderA│ │ConcreteBuilderB│
├──────────────┤ ├──────────────┤
│+buildPartA() │ │+buildPartA() │
│+buildPartB() │ │+buildPartB() │
│+getResult() │ │+getResult() │
└──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Product │ │ Product │
│ (不同表示) │ │ (不同表示) │
└──────────────┘ └──────────────┘3.3 时序图
Client Director Builder Product
│ │ │ │
│─construct()───>│ │ │
│ │─buildPartA()──>│ │
│ │ │──new PartA──>│
│ │─buildPartB()──>│ │
│ │ │──new PartB──>│
│ │─buildPartC()──>│ │
│ │ │──new PartC──>│
│ │─getResult()───>│ │
│ │<──Product──────│ │
│<──Product──────│ │ │四、代码实现
4.1 基础实现
4.1.1 传统建造者模式
java
/**
* 传统建造者模式 —— 电脑组装
* 包含 Director 角色,控制构建步骤
*/
public class TraditionalBuilderDemo {
// ========== 1. 产品:电脑 ==========
public static class Computer {
private String cpu;
private String ram;
private String storage;
private String gpu;
private String os;
// 构造器为 package-private,只有 Builder 能创建
Computer() {}
public void setCpu(String cpu) { this.cpu = cpu; }
public void setRam(String ram) { this.ram = ram; }
public void setStorage(String storage) { this.storage = storage; }
public void setGpu(String gpu) { this.gpu = gpu; }
public void setOs(String os) { this.os = os; }
@Override
public String toString() {
return "Computer{cpu='" + cpu + "', ram='" + ram + "', storage='" + storage +
"', gpu='" + gpu + "', os='" + os + "'}";
}
}
// ========== 2. 抽象建造者 ==========
public interface ComputerBuilder {
void buildCpu();
void buildRam();
void buildStorage();
void buildGpu();
void installOs();
Computer getResult();
}
// ========== 3. 具体建造者:游戏电脑 ==========
public static class GamingComputerBuilder implements ComputerBuilder {
private final Computer computer = new Computer();
@Override
public void buildCpu() {
computer.setCpu("Intel Core i9-14900K");
System.out.println(" [游戏电脑] 安装顶级 CPU: i9-14900K");
}
@Override
public void buildRam() {
computer.setRam("64GB DDR5 6000MHz");
System.out.println(" [游戏电脑] 安装大内存: 64GB DDR5");
}
@Override
public void buildStorage() {
computer.setStorage("2TB NVMe SSD");
System.out.println(" [游戏电脑] 安装高速固态: 2TB NVMe");
}
@Override
public void buildGpu() {
computer.setGpu("NVIDIA RTX 4090");
System.out.println(" [游戏电脑] 安装顶级显卡: RTX 4090");
}
@Override
public void installOs() {
computer.setOs("Windows 11 Pro");
System.out.println(" [游戏电脑] 安装系统: Windows 11 Pro");
}
@Override
public Computer getResult() {
return computer;
}
}
// ========== 4. 具体建造者:办公电脑 ==========
public static class OfficeComputerBuilder implements ComputerBuilder {
private final Computer computer = new Computer();
@Override
public void buildCpu() {
computer.setCpu("Intel Core i5-13400");
System.out.println(" [办公电脑] 安装主流 CPU: i5-13400");
}
@Override
public void buildRam() {
computer.setRam("16GB DDR4 3200MHz");
System.out.println(" [办公电脑] 安装标准内存: 16GB DDR4");
}
@Override
public void buildStorage() {
computer.setStorage("512GB SSD");
System.out.println(" [办公电脑] 安装固态硬盘: 512GB SSD");
}
@Override
public void buildGpu() {
computer.setGpu("集成显卡");
System.out.println(" [办公电脑] 使用集成显卡");
}
@Override
public void installOs() {
computer.setOs("Windows 11 Home");
System.out.println(" [办公电脑] 安装系统: Windows 11 Home");
}
@Override
public Computer getResult() {
return computer;
}
}
// ========== 5. 指挥者:控制构建步骤 ==========
public static class ComputerDirector {
public Computer buildGamingPC(ComputerBuilder builder) {
System.out.println("\n===== 开始组装游戏电脑 =====");
builder.buildCpu();
builder.buildRam();
builder.buildGpu();
builder.buildStorage();
builder.installOs();
System.out.println("===== 游戏电脑组装完成 =====\n");
return builder.getResult();
}
public Computer buildOfficePC(ComputerBuilder builder) {
System.out.println("\n===== 开始组装办公电脑 =====");
builder.buildCpu();
builder.buildRam();
builder.buildStorage(); // 办公电脑先装存储再装显卡
builder.buildGpu();
builder.installOs();
System.out.println("===== 办公电脑组装完成 =====\n");
return builder.getResult();
}
}
// ========== 6. 测试 ==========
public static void main(String[] args) {
ComputerDirector director = new ComputerDirector();
// 构建游戏电脑
ComputerBuilder gamingBuilder = new GamingComputerBuilder();
Computer gamingPC = director.buildGamingPC(gamingBuilder);
System.out.println("最终产品: " + gamingPC);
// 构建办公电脑
ComputerBuilder officeBuilder = new OfficeComputerBuilder();
Computer officePC = director.buildOfficePC(officeBuilder);
System.out.println("最终产品: " + officePC);
}
}4.2 进阶实现
4.2.1 链式调用建造者(Lombok @Builder 原理)
java
/**
* 链式调用建造者 —— 现代 Java 中最常用的建造者变体
* 省略了 Director 角色,Builder 本身控制构建流程
*
* 这正是 Lombok @Builder 注解的底层原理
*/
public class FluentBuilderDemo {
// ========== 产品:不可变对象 ==========
public static class HttpRequest {
// 所有字段都是 final,保证不可变性
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private final int connectTimeout;
private final int readTimeout;
private final boolean followRedirects;
private final int maxRetries;
// 私有构造器,只能通过 Builder 创建
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
this.body = builder.body;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.followRedirects = builder.followRedirects;
this.maxRetries = builder.maxRetries;
}
@Override
public String toString() {
return "HttpRequest{url='" + url + "', method='" + method + "', headers=" + headers +
", body='" + body + "', connectTimeout=" + connectTimeout +
", readTimeout=" + readTimeout + ", followRedirects=" + followRedirects +
", maxRetries=" + maxRetries + "}";
}
// ========== 静态内部 Builder 类 ==========
public static class Builder {
// 必填参数
private final String url;
private final String method;
// 可选参数(带默认值)
private Map<String, String> headers = new HashMap<>();
private String body = "";
private int connectTimeout = 5000;
private int readTimeout = 10000;
private boolean followRedirects = true;
private int maxRetries = 0;
// 构造器接收必填参数
public Builder(String url, String method) {
this.url = url;
this.method = method;
}
// 每个 setter 返回 this,实现链式调用
public Builder addHeader(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder connectTimeout(int timeoutMs) {
this.connectTimeout = timeoutMs;
return this;
}
public Builder readTimeout(int timeoutMs) {
this.readTimeout = timeoutMs;
return this;
}
public Builder followRedirects(boolean follow) {
this.followRedirects = follow;
return this;
}
public Builder maxRetries(int maxRetries) {
this.maxRetries = maxRetries;
return this;
}
// build() 方法完成构建,可以在此处做参数校验
public HttpRequest build() {
// 构建前校验
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL 不能为空");
}
if (connectTimeout <= 0 || readTimeout <= 0) {
throw new IllegalArgumentException("超时时间必须大于 0");
}
return new HttpRequest(this);
}
}
}
// ========== 测试 ==========
public static void main(String[] args) {
// 链式调用,优雅构建复杂对象
HttpRequest request = new HttpRequest.Builder("https://api.example.com/users", "POST")
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Bearer token123")
.body("{\"name\": \"张三\", \"age\": 25}")
.connectTimeout(3000)
.readTimeout(5000)
.maxRetries(3)
.followRedirects(false)
.build();
System.out.println(request);
// 最简构建(只传必填参数)
HttpRequest simpleRequest = new HttpRequest.Builder("https://api.example.com/health", "GET")
.build();
System.out.println(simpleRequest);
}
}4.2.2 Lombok @Builder 等价实现
java
/**
* 模拟 Lombok @Builder 的完整实现
*
* Lombok @Builder 注解在编译时会生成类似以下的代码:
*
* @Builder
* public class User {
* private String name;
* private int age;
* private String email;
* }
*/
public class LombokStyleBuilder {
public static class User {
private final String name;
private final int age;
private final String email;
private final String phone;
private final String address;
// Lombok 生成的构造器
User(String name, int age, String email, String phone, String address) {
this.name = name;
this.age = age;
this.email = email;
this.phone = phone;
this.address = address;
}
// Lombok 生成的静态 builder() 方法
public static UserBuilder builder() {
return new UserBuilder();
}
// Lombok 生成的 Builder 类
public static class UserBuilder {
private String name;
private int age;
private String email;
private String phone;
private String address;
UserBuilder() {}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder email(String email) {
this.email = email;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
public User build() {
return new User(name, age, email, phone, address);
}
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + ", email='" + email +
"', phone='" + phone + "', address='" + address + "'}";
}
}
public static void main(String[] args) {
User user = User.builder()
.name("张三")
.age(30)
.email("zhangsan@example.com")
.phone("13800138000")
.address("北京市朝阳区")
.build();
System.out.println(user);
}
}4.3 生产级实现(Spring Boot 场景)
java
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
/**
* 生产级 SQL 查询建造器 —— 类似于 MyBatis-Plus 的 QueryWrapper
*
* 业务场景:动态构建 SQL 查询,根据条件选择性添加 WHERE 子句
*/
public class SqlQueryBuilder {
// ========== 产品:SQL 查询 ==========
public static class SqlQuery {
private final String select;
private final String from;
private final List<String> whereClauses;
private final List<String> orderByColumns;
private final String groupBy;
private final Integer limit;
private final Integer offset;
private final List<Object> parameters;
private SqlQuery(Builder builder) {
this.select = builder.select;
this.from = builder.from;
this.whereClauses = new ArrayList<>(builder.whereClauses);
this.orderByColumns = new ArrayList<>(builder.orderByColumns);
this.groupBy = builder.groupBy;
this.limit = builder.limit;
this.offset = builder.offset;
this.parameters = new ArrayList<>(builder.parameters);
}
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ").append(select != null ? select : "*");
sql.append(" FROM ").append(from);
if (!whereClauses.isEmpty()) {
sql.append(" WHERE ").append(String.join(" AND ", whereClauses));
}
if (groupBy != null) {
sql.append(" GROUP BY ").append(groupBy);
}
if (!orderByColumns.isEmpty()) {
sql.append(" ORDER BY ").append(String.join(", ", orderByColumns));
}
if (limit != null) {
sql.append(" LIMIT ").append(limit);
}
if (offset != null) {
sql.append(" OFFSET ").append(offset);
}
return sql.toString();
}
public List<Object> getParameters() {
return Collections.unmodifiableList(parameters);
}
}
// ========== Builder ==========
public static class Builder {
private String select;
private final String from;
private final List<String> whereClauses = new ArrayList<>();
private final List<String> orderByColumns = new ArrayList<>();
private String groupBy;
private Integer limit;
private Integer offset;
private final List<Object> parameters = new ArrayList<>();
public Builder(String from) {
this.from = from;
}
public Builder select(String... columns) {
this.select = String.join(", ", columns);
return this;
}
public Builder where(String clause) {
this.whereClauses.add(clause);
return this;
}
public Builder where(String clause, Object param) {
this.whereClauses.add(clause);
this.parameters.add(param);
return this;
}
public Builder andEq(String column, Object value) {
if (value != null) {
this.whereClauses.add(column + " = ?");
this.parameters.add(value);
}
return this;
}
public Builder andLike(String column, String value) {
if (value != null && !value.isEmpty()) {
this.whereClauses.add(column + " LIKE ?");
this.parameters.add("%" + value + "%");
}
return this;
}
public Builder andBetween(String column, Object start, Object end) {
if (start != null && end != null) {
this.whereClauses.add(column + " BETWEEN ? AND ?");
this.parameters.add(start);
this.parameters.add(end);
}
return this;
}
public Builder andIn(String column, List<?> values) {
if (values != null && !values.isEmpty()) {
String placeholders = String.join(", ", Collections.nCopies(values.size(), "?"));
this.whereClauses.add(column + " IN (" + placeholders + ")");
this.parameters.addAll(values);
}
return this;
}
public Builder orderByAsc(String column) {
this.orderByColumns.add(column + " ASC");
return this;
}
public Builder orderByDesc(String column) {
this.orderByColumns.add(column + " DESC");
return this;
}
public Builder groupBy(String column) {
this.groupBy = column;
return this;
}
public Builder limit(int limit) {
this.limit = limit;
return this;
}
public Builder offset(int offset) {
this.offset = offset;
return this;
}
public Builder page(int pageNum, int pageSize) {
this.offset = (pageNum - 1) * pageSize;
this.limit = pageSize;
return this;
}
public SqlQuery build() {
if (from == null || from.isEmpty()) {
throw new IllegalArgumentException("FROM 子句不能为空");
}
return new SqlQuery(this);
}
}
// ========== 测试 ==========
public static void main(String[] args) {
// 动态构建复杂查询
SqlQuery query = new SqlQuery.Builder("users u")
.select("u.id", "u.name", "u.email", "u.created_at")
.andEq("u.status", "ACTIVE")
.andLike("u.name", "张")
.andBetween("u.age", 18, 35)
.andIn("u.department", List.of("DEV", "QA", "OPS"))
.orderByDesc("u.created_at")
.page(1, 20)
.build();
System.out.println("SQL: " + query.toSql());
System.out.println("参数: " + query.getParameters());
}
}五、优缺点
优点
| 优点 | 说明 |
|---|---|
| 参数灵活 | 客户端可以按需设置参数,不必为所有参数传值 |
| 代码可读性好 | 链式调用语义清晰,如 .name("张三").age(30) |
| 不可变对象 | 可以在 build() 时创建不可变对象,保证线程安全 |
| 构建过程可控 | 在 build() 中可以做参数校验、默认值设置 |
| 符合单一职责原则 | 构建逻辑与业务逻辑分离 |
缺点
| 缺点 | 说明 |
|---|---|
| 代码量增加 | 需要编写 Builder 类,代码量比直接使用构造器多 |
| 增加类数量 | 每个产品类通常需要一个 Builder 内部类 |
| 不适合简单对象 | 如果只有 2-3 个参数,使用建造者反而过度设计 |
六、适用场景
- 构造参数过多(通常超过 4 个):如 HTTP 请求构建、数据库连接配置
- 参数可选且有默认值:如用户搜索条件、报表配置
- 需要创建不可变对象:如配置对象、请求对象、DTO
- 需要构建不同表示:如导出不同格式的报表(PDF、Excel、CSV)
- SQL 动态查询构建:如 MyBatis-Plus 的 QueryWrapper、JPA 的 Specification
- 复杂对象分步创建:如文档生成器、邮件构建器
- 需要参数校验:在 build() 方法中统一校验,避免创建非法对象
七、JDK / Spring 框架中的实际应用
7.1 JDK 中的建造者
1. StringBuilder / StringBuffer
- 最经典的建造者模式实现
- append() 方法返回 this,链式调用
- toString() 相当于 build()
2. java.util.stream.Stream.Builder
- Stream API 中的建造者,用于构建流
3. java.lang.ProcessBuilder
- 用于构建操作系统进程
4. java.util.Calendar.Builder (JDK 8+)
- Calendar 的建造者
5. javax.swing.GroupLayout
- 布局管理器的建造者风格 API7.2 Spring 框架中的建造者
1. RestTemplate / WebClient
- WebClient.create().uri(...).header(...).retrieve() 链式调用
2. UriComponentsBuilder
- 构建 URI,支持链式添加参数
3. MockMvcRequestBuilders
- Spring Test 中的请求构建器
4. ResponseEntity Builder
- ResponseEntity.ok().header(...).body(...)
5. Spring Security 的 HttpSecurity
- http.authorizeRequests().anyRequest().authenticated()...7.3 知名第三方库
1. Lombok @Builder
- 编译期自动生成建造者代码
2. OkHttp Request.Builder
- new Request.Builder().url(...).header(...).build()
3. Retrofit
- 基于注解的 HTTP 客户端,内部使用建造者模式
4. MyBatis-Plus QueryWrapper
- LambdaQueryWrapper<User>().eq(User::getName, "张三").like(User::getEmail, "gmail")
5. Apache HttpClient
- RequestBuilder.get().setUri(...).build()八、与其他模式的关系
| 相关模式 | 关系说明 |
|---|---|
| 工厂方法模式 | 工厂方法关注"创建什么",建造者关注"怎么创建"。工厂方法一步创建,建造者分步创建 |
| 抽象工厂模式 | 抽象工厂创建产品族,建造者创建复杂产品。两者可以组合:抽象工厂返回建造者 |
| 组合模式 | 建造者常用于构建组合模式中的树形结构 |
| 原型模式 | 建造者可以配合原型模式:先用原型克隆,再用建造者修改 |
| 单例模式 | 建造者创建的通常不是单例,但建造者本身可以是单例 |
九、面试常见问题
Q1:建造者模式和工厂模式有什么区别?
答案:
| 对比维度 | 工厂模式 | 建造者模式 |
|---|---|---|
| 创建方式 | 一步创建 | 分步创建 |
| 关注点 | 创建什么产品 | 如何组装产品 |
| 参数复杂度 | 参数较少 | 参数多且复杂 |
| 产品类型 | 返回不同类型(多态) | 返回同一类型的不同表示 |
| 典型场景 | 数据库连接、日志记录器 | HTTP 请求、SQL 查询、配置对象 |
一句话:工厂模式是"批发"(一次创建完整对象),建造者模式是"零售"(一步步组装)。
Q2:Lombok @Builder 是如何实现的?
答案: Lombok 使用 APT(Annotation Processing Tool) 在编译期生成代码:
- 为被注解的类生成一个静态内部类
XxxBuilder - 为每个字段生成对应的 setter 方法(返回
XxxBuilder) - 生成
build()方法,调用全参构造器创建对象 - 生成静态
builder()方法返回XxxBuilder实例
本质是编译期代码生成,不影响运行时性能。
Q3:Director(指挥者)在建造者模式中是否必须?
答案: 不是必须的。在现代 Java 实践中,Director 角色常被省略,Builder 自己控制构建流程。两种形式各有适用场景:
- 有 Director:构建步骤固定且顺序重要时(如电脑组装、文档生成),Director 封装构建顺序
- 无 Director:参数可选且顺序无关时(如 HTTP 请求、配置对象),链式调用更灵活
Q4:建造者模式和 setter 方式有什么区别?
答案:
| 对比维度 | 建造者模式 | Setter 方式 |
|---|---|---|
| 不可变性 | 可以创建不可变对象(final 字段) | 对象可变,线程不安全 |
| 一致性 | build() 时一次性创建,保证原子性 | 设置过程中可能处于不一致状态 |
| 参数校验 | 在 build() 中统一校验 | 分散在各 setter 中,难以保证 |
| 代码风格 | 链式调用,语义清晰 | 逐行 set,代码冗长 |
Q5:建造者模式在什么情况下不适用?
答案:
- 参数很少(少于 3 个):使用构造器或静态工厂方法更简单
- 参数都是必填的:直接用构造器更清晰
- 性能极度敏感:建造者会创建额外的 Builder 对象(但通常可忽略)
- 对象结构简单:没有复杂初始化逻辑
总结
建造者模式是处理"参数爆炸"的最佳方案,在现代 Java 开发中极为常用。核心要点:
- 链式调用是现代建造者最流行的形式,Lombok
@Builder就是最佳实践 - 不可变对象是建造者模式的重要应用场景,保证线程安全
- 参数校验放在
build()方法中,可以避免创建非法对象 - Director 角色非必须,简单场景用链式调用即可
- 实际开发中,SQL 查询构建器、HTTP 请求构建器是最常见的生产级应用
