深入理解设计模式之建造者模式(Builder Pattern)
一、引言
在软件开发中,我们经常需要创建复杂的对象,这些对象包含众多属性,而且有些属性是必填的,有些是可选的。如果使用传统的构造函数方式,会面临以下问题:
- 构造函数参数过多:当对象有10个以上的属性时,构造函数会变得难以阅读和维护
- 参数顺序容易出错:多个相同类型的参数容易传错位置
- 可选参数处理困难:需要提供多个重载构造函数
- 对象一致性难以保证:使用setter方法分步设置,对象在完全构建前可能处于不一致状态
建造者模式为这类问题提供了优雅的解决方案。它就像现实中的装修公司:你想装修房子,告诉设计师你的需求(要几个房间、什么风格、用什么材料),设计师会一步步帮你规划和施工,最后交付一个完整的房子,而不是让你自己去组装每个零件。
本文将深入探讨建造者模式的原理、实现方式,并结合StringBuilder、Lombok、OkHttp等框架应用,帮助你全面掌握这一重要的设计模式。
二、什么是建造者模式
2.1 定义
建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
2.2 核心思想
- 分离构建和表示:将对象的构建过程独立出来
- 链式调用:通过链式调用设置参数,代码更优雅
- 不可变对象:构建完成后对象不可修改,线程安全
- 参数校验:在build()方法中统一校验,保证对象一致性
2.3 模式结构
经典建造者模式:
┌─────────────────┐
│ Director │ 指导者(可选)
├─────────────────┤
│- builder │
│+ construct() │ 控制构建流程
└────────┬────────┘
│ 使用
▼
┌─────────────────┐
│ <<interface>> │
│ Builder │ 抽象建造者
├─────────────────┤
│+ buildPartA() │
│+ buildPartB() │
│+ getResult() │
└────────┬────────┘
△
│ 实现
┌────┴────┐
│ │
┌───┴──────┐ │
│Concrete │ │ 具体建造者
│Builder │ │
└──────────┘ │
│ 创建
▼
┌─────────┐
│ Product │ 产品
└─────────┘
简化建造者模式(现代常用):
┌──────────────────────────────┐
│ Product │
├──────────────────────────────┤
│- field1 │
│- field2 │
│- field3 │
│ │
│+ Product(Builder builder) │ 私有构造
└──────────────────────────────┘
△
│ 内部类
┌────────┴──────────────────────┐
│ Product.Builder │
├───────────────────────────────┤
│- field1 │
│- field2 │
│- field3 │
│ │
│+ field1(value): Builder │ 链式调用
│+ field2(value): Builder │
│+ field3(value): Builder │
│+ build(): Product │ 构建对象
└───────────────────────────────┘
使用方式:
Product product = Product.builder()
.field1(value1)
.field2(value2)
.field3(value3)
.build();
2.4 建造者模式 vs 构造函数
传统构造函数:
new User("张三", 25, "男", "北京", "138****8888",
"zhangsan@example.com", "软件工程师", "未婚", "汉族");
❌ 参数过多,容易出错
❌ 参数顺序固定
❌ 可选参数需要多个重载
建造者模式:
User user = User.builder()
.name("张三")
.age(25)
.gender("男")
.city("北京")
.phone("138****8888")
.email("zhangsan@example.com")
.build();
✓ 参数清晰
✓ 链式调用优雅
✓ 可选参数灵活
三、基础示例
3.1 场景:电脑配置
构建一台电脑,需要选择CPU、内存、硬盘、显卡等组件。
产品类:
java
/**
* 产品类:电脑
*/
public class Computer {
// 必选参数
private final String cpu;
private final String ram;
// 可选参数
private final String storage;
private final String gpu;
private final String monitor;
private final String keyboard;
private final String mouse;
/**
* 私有构造函数,只能通过Builder创建
*/
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.gpu = builder.gpu;
this.monitor = builder.monitor;
this.keyboard = builder.keyboard;
this.mouse = builder.mouse;
}
/**
* 静态方法创建Builder
*/
public static Builder builder() {
return new Builder();
}
/**
* 内部建造者类
*/
public static class Builder {
// 必选参数
private String cpu;
private String ram;
// 可选参数(设置默认值)
private String storage = "256GB SSD";
private String gpu = "集成显卡";
private String monitor = "无";
private String keyboard = "标准键盘";
private String mouse = "标准鼠标";
/**
* 必选参数通过构造函数或单独的方法设置
*/
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder ram(String ram) {
this.ram = ram;
return this;
}
/**
* 可选参数通过方法设置
*/
public Builder storage(String storage) {
this.storage = storage;
return this;
}
public Builder gpu(String gpu) {
this.gpu = gpu;
return this;
}
public Builder monitor(String monitor) {
this.monitor = monitor;
return this;
}
public Builder keyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder mouse(String mouse) {
this.mouse = mouse;
return this;
}
/**
* 构建对象
*/
public Computer build() {
// 参数校验
if (cpu == null || cpu.isEmpty()) {
throw new IllegalArgumentException("CPU不能为空");
}
if (ram == null || ram.isEmpty()) {
throw new IllegalArgumentException("内存不能为空");
}
return new Computer(this);
}
}
@Override
public String toString() {
return "Computer配置:\n" +
" CPU: " + cpu + "\n" +
" 内存: " + ram + "\n" +
" 硬盘: " + storage + "\n" +
" 显卡: " + gpu + "\n" +
" 显示器: " + monitor + "\n" +
" 键盘: " + keyboard + "\n" +
" 鼠标: " + mouse;
}
}
客户端使用:
java
public class ComputerBuilderDemo {
public static void main(String[] args) {
// 场景1:办公电脑(只配置必选参数)
System.out.println("=== 办公电脑 ===");
Computer officeComputer = Computer.builder()
.cpu("Intel i5-12400")
.ram("16GB DDR4")
.build();
System.out.println(officeComputer);
// 场景2:游戏电脑(配置所有参数)
System.out.println("\n=== 游戏电脑 ===");
Computer gamingComputer = Computer.builder()
.cpu("AMD Ryzen 7 5800X")
.ram("32GB DDR4")
.storage("1TB NVMe SSD")
.gpu("NVIDIA RTX 4070")
.monitor("27寸 2K 165Hz")
.keyboard("机械键盘")
.mouse("游戏鼠标")
.build();
System.out.println(gamingComputer);
// 场景3:设计师电脑
System.out.println("\n=== 设计师电脑 ===");
Computer designerComputer = Computer.builder()
.cpu("Intel i9-13900K")
.ram("64GB DDR5")
.storage("2TB NVMe SSD")
.gpu("NVIDIA RTX 4090")
.monitor("32寸 4K专业显示器")
.build();
System.out.println(designerComputer);
}
}
输出:
=== 办公电脑 ===
Computer配置:
CPU: Intel i5-12400
内存: 16GB DDR4
硬盘: 256GB SSD
显卡: 集成显卡
显示器: 无
键盘: 标准键盘
鼠标: 标准鼠标
=== 游戏电脑 ===
Computer配置:
CPU: AMD Ryzen 7 5800X
内存: 32GB DDR4
硬盘: 1TB NVMe SSD
显卡: NVIDIA RTX 4070
显示器: 27寸 2K 165Hz
键盘: 机械键盘
鼠标: 游戏鼠标
=== 设计师电脑 ===
Computer配置:
CPU: Intel i9-13900K
内存: 64GB DDR5
硬盘: 2TB NVMe SSD
显卡: NVIDIA RTX 4090
显示器: 32寸 4K专业显示器
键盘: 标准键盘
鼠标: 标准鼠标
3.2 场景:HTTP请求构建
构建一个HTTP请求,包含URL、方法、请求头、请求体等。
java
import java.util.*;
/**
* HTTP请求对象
*/
public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final Map<String, String> params;
private final String body;
private final int timeout;
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
this.params = Collections.unmodifiableMap(new HashMap<>(builder.params));
this.body = builder.body;
this.timeout = builder.timeout;
}
public static Builder builder() {
return new Builder();
}
/**
* 建造者类
*/
public static class Builder {
private String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private Map<String, String> params = new HashMap<>();
private String body;
private int timeout = 30000; // 默认30秒
public Builder url(String url) {
this.url = url;
return this;
}
public Builder method(String method) {
this.method = method;
return this;
}
public Builder get() {
return method("GET");
}
public Builder post() {
return method("POST");
}
public Builder put() {
return method("PUT");
}
public Builder delete() {
return method("DELETE");
}
public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder headers(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}
public Builder param(String key, String value) {
this.params.put(key, value);
return this;
}
public Builder params(Map<String, String> params) {
this.params.putAll(params);
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public HttpRequest build() {
// 校验
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
// 自动添加Content-Type(如果有body)
if (body != null && !headers.containsKey("Content-Type")) {
headers.put("Content-Type", "application/json");
}
return new HttpRequest(this);
}
}
/**
* 执行请求(模拟)
*/
public String execute() {
StringBuilder sb = new StringBuilder();
sb.append("执行HTTP请求:\n");
sb.append(" URL: ").append(url);
// 拼接参数
if (!params.isEmpty()) {
sb.append("?");
params.forEach((k, v) -> sb.append(k).append("=").append(v).append("&"));
sb.setLength(sb.length() - 1); // 移除最后的&
}
sb.append("\n");
sb.append(" 方法: ").append(method).append("\n");
if (!headers.isEmpty()) {
sb.append(" 请求头:\n");
headers.forEach((k, v) -> sb.append(" ").append(k).append(": ").append(v).append("\n"));
}
if (body != null) {
sb.append(" 请求体: ").append(body).append("\n");
}
sb.append(" 超时: ").append(timeout).append("ms\n");
return sb.toString();
}
// Getters
public String getUrl() { return url; }
public String getMethod() { return method; }
public Map<String, String> getHeaders() { return headers; }
public Map<String, String> getParams() { return params; }
public String getBody() { return body; }
public int getTimeout() { return timeout; }
}
/**
* 测试
*/
class HttpRequestDemo {
public static void main(String[] args) {
// 场景1:简单GET请求
System.out.println("=== 场景1:GET请求 ===");
HttpRequest request1 = HttpRequest.builder()
.url("https://api.example.com/users")
.get()
.param("page", "1")
.param("size", "10")
.build();
System.out.println(request1.execute());
// 场景2:POST请求with JSON
System.out.println("\n=== 场景2:POST请求 ===");
HttpRequest request2 = HttpRequest.builder()
.url("https://api.example.com/users")
.post()
.header("Authorization", "Bearer token123")
.body("{\"name\":\"张三\",\"age\":25}")
.timeout(5000)
.build();
System.out.println(request2.execute());
// 场景3:复杂请求
System.out.println("\n=== 场景3:复杂请求 ===");
HttpRequest request3 = HttpRequest.builder()
.url("https://api.example.com/orders")
.put()
.header("Authorization", "Bearer token456")
.header("X-Request-ID", "REQ-123")
.param("orderId", "ORDER001")
.body("{\"status\":\"completed\"}")
.timeout(10000)
.build();
System.out.println(request3.execute());
}
}
四、实际生产场景应用
4.1 场景:SQL查询构建器
在实际开发中,动态构建SQL查询是常见需求。
java
import java.util.*;
/**
* SQL查询构建器
*/
public class SqlQuery {
private final String table;
private final List<String> columns;
private final List<String> whereClauses;
private final List<String> orderByColumns;
private final List<String> groupByColumns;
private final Integer limit;
private final Integer offset;
private SqlQuery(Builder builder) {
this.table = builder.table;
this.columns = new ArrayList<>(builder.columns);
this.whereClauses = new ArrayList<>(builder.whereClauses);
this.orderByColumns = new ArrayList<>(builder.orderByColumns);
this.groupByColumns = new ArrayList<>(builder.groupByColumns);
this.limit = builder.limit;
this.offset = builder.offset;
}
public static Builder select(String... columns) {
return new Builder().columns(columns);
}
/**
* SQL构建器
*/
public static class Builder {
private String table;
private List<String> columns = new ArrayList<>();
private List<String> whereClauses = new ArrayList<>();
private List<String> orderByColumns = new ArrayList<>();
private List<String> groupByColumns = new ArrayList<>();
private Integer limit;
private Integer offset;
public Builder columns(String... columns) {
this.columns.addAll(Arrays.asList(columns));
return this;
}
public Builder from(String table) {
this.table = table;
return this;
}
public Builder where(String clause) {
this.whereClauses.add(clause);
return this;
}
public Builder whereEquals(String column, Object value) {
if (value instanceof String) {
whereClauses.add(column + " = '" + value + "'");
} else {
whereClauses.add(column + " = " + value);
}
return this;
}
public Builder whereLike(String column, String pattern) {
whereClauses.add(column + " LIKE '%" + pattern + "%'");
return this;
}
public Builder whereIn(String column, Object... values) {
StringBuilder sb = new StringBuilder();
sb.append(column).append(" IN (");
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof String) {
sb.append("'").append(values[i]).append("'");
} else {
sb.append(values[i]);
}
if (i < values.length - 1) {
sb.append(", ");
}
}
sb.append(")");
whereClauses.add(sb.toString());
return this;
}
public Builder orderBy(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... columns) {
this.groupByColumns.addAll(Arrays.asList(columns));
return this;
}
public Builder limit(int limit) {
this.limit = limit;
return this;
}
public Builder offset(int offset) {
this.offset = offset;
return this;
}
public SqlQuery build() {
if (table == null || table.isEmpty()) {
throw new IllegalArgumentException("表名不能为空");
}
if (columns.isEmpty()) {
columns.add("*");
}
return new SqlQuery(this);
}
}
/**
* 生成SQL字符串
*/
public String toSql() {
StringBuilder sql = new StringBuilder();
// SELECT
sql.append("SELECT ");
sql.append(String.join(", ", columns));
// FROM
sql.append(" FROM ").append(table);
// WHERE
if (!whereClauses.isEmpty()) {
sql.append(" WHERE ");
sql.append(String.join(" AND ", whereClauses));
}
// GROUP BY
if (!groupByColumns.isEmpty()) {
sql.append(" GROUP BY ");
sql.append(String.join(", ", groupByColumns));
}
// ORDER BY
if (!orderByColumns.isEmpty()) {
sql.append(" ORDER BY ");
sql.append(String.join(", ", orderByColumns));
}
// LIMIT
if (limit != null) {
sql.append(" LIMIT ").append(limit);
}
// OFFSET
if (offset != null) {
sql.append(" OFFSET ").append(offset);
}
return sql.toString();
}
}
/**
* 测试SQL构建器
*/
class SqlQueryDemo {
public static void main(String[] args) {
// 场景1:简单查询
System.out.println("=== 场景1:查询所有用户 ===");
SqlQuery query1 = SqlQuery.select("*")
.from("users")
.build();
System.out.println(query1.toSql());
// 场景2:条件查询
System.out.println("\n=== 场景2:条件查询 ===");
SqlQuery query2 = SqlQuery.select("id", "name", "email")
.from("users")
.whereEquals("age", 25)
.whereEquals("city", "北京")
.orderByDesc("created_at")
.limit(10)
.build();
System.out.println(query2.toSql());
// 场景3:复杂查询
System.out.println("\n=== 场景3:复杂查询 ===");
SqlQuery query3 = SqlQuery.select("category", "COUNT(*) as count", "AVG(price) as avg_price")
.from("products")
.whereIn("status", "active", "pending")
.whereLike("name", "手机")
.groupBy("category")
.orderBy("count")
.limit(20)
.offset(10)
.build();
System.out.println(query3.toSql());
// 场景4:分页查询
System.out.println("\n=== 场景4:分页查询 ===");
int page = 2;
int pageSize = 15;
SqlQuery query4 = SqlQuery.select("*")
.from("orders")
.whereEquals("user_id", 123)
.orderByDesc("order_date")
.limit(pageSize)
.offset((page - 1) * pageSize)
.build();
System.out.println(query4.toSql());
}
}
4.2 场景:系统配置构建器
应用系统的配置对象,包含数据库、缓存、日志等配置。
java
/**
* 应用配置类
*/
public class ApplicationConfig {
// 数据库配置
private final String dbUrl;
private final String dbUsername;
private final String dbPassword;
private final int dbPoolSize;
// 缓存配置
private final boolean cacheEnabled;
private final String cacheType;
private final int cacheMaxSize;
// 日志配置
private final String logLevel;
private final String logPath;
// 服务器配置
private final int serverPort;
private final String contextPath;
private ApplicationConfig(Builder builder) {
this.dbUrl = builder.dbUrl;
this.dbUsername = builder.dbUsername;
this.dbPassword = builder.dbPassword;
this.dbPoolSize = builder.dbPoolSize;
this.cacheEnabled = builder.cacheEnabled;
this.cacheType = builder.cacheType;
this.cacheMaxSize = builder.cacheMaxSize;
this.logLevel = builder.logLevel;
this.logPath = builder.logPath;
this.serverPort = builder.serverPort;
this.contextPath = builder.contextPath;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
// 数据库配置
private String dbUrl;
private String dbUsername;
private String dbPassword;
private int dbPoolSize = 10;
// 缓存配置
private boolean cacheEnabled = false;
private String cacheType = "memory";
private int cacheMaxSize = 1000;
// 日志配置
private String logLevel = "INFO";
private String logPath = "./logs";
// 服务器配置
private int serverPort = 8080;
private String contextPath = "/";
// 数据库配置方法
public Builder database(String url, String username, String password) {
this.dbUrl = url;
this.dbUsername = username;
this.dbPassword = password;
return this;
}
public Builder dbPoolSize(int poolSize) {
this.dbPoolSize = poolSize;
return this;
}
// 缓存配置方法
public Builder enableCache(boolean enabled) {
this.cacheEnabled = enabled;
return this;
}
public Builder cacheType(String type) {
this.cacheType = type;
return this;
}
public Builder cacheMaxSize(int maxSize) {
this.cacheMaxSize = maxSize;
return this;
}
// 日志配置方法
public Builder logLevel(String level) {
this.logLevel = level;
return this;
}
public Builder logPath(String path) {
this.logPath = path;
return this;
}
// 服务器配置方法
public Builder serverPort(int port) {
this.serverPort = port;
return this;
}
public Builder contextPath(String path) {
this.contextPath = path;
return this;
}
public ApplicationConfig build() {
// 校验必填参数
if (dbUrl == null || dbUrl.isEmpty()) {
throw new IllegalArgumentException("数据库URL不能为空");
}
// 校验端口号
if (serverPort < 1 || serverPort > 65535) {
throw new IllegalArgumentException("端口号必须在1-65535之间");
}
return new ApplicationConfig(this);
}
}
@Override
public String toString() {
return "应用配置:\n" +
"数据库配置:\n" +
" URL: " + dbUrl + "\n" +
" 用户名: " + dbUsername + "\n" +
" 连接池大小: " + dbPoolSize + "\n" +
"缓存配置:\n" +
" 启用: " + cacheEnabled + "\n" +
" 类型: " + cacheType + "\n" +
" 最大容量: " + cacheMaxSize + "\n" +
"日志配置:\n" +
" 级别: " + logLevel + "\n" +
" 路径: " + logPath + "\n" +
"服务器配置:\n" +
" 端口: " + serverPort + "\n" +
" 上下文路径: " + contextPath;
}
}
/**
* 测试配置构建器
*/
class ConfigBuilderDemo {
public static void main(String[] args) {
// 开发环境配置
System.out.println("=== 开发环境 ===");
ApplicationConfig devConfig = ApplicationConfig.builder()
.database("jdbc:mysql://localhost:3306/dev_db", "root", "password")
.logLevel("DEBUG")
.serverPort(8080)
.build();
System.out.println(devConfig);
// 生产环境配置
System.out.println("\n=== 生产环境 ===");
ApplicationConfig prodConfig = ApplicationConfig.builder()
.database("jdbc:mysql://prod-server:3306/prod_db", "admin", "prod_pass")
.dbPoolSize(50)
.enableCache(true)
.cacheType("redis")
.cacheMaxSize(10000)
.logLevel("WARN")
.logPath("/var/log/app")
.serverPort(80)
.contextPath("/api")
.build();
System.out.println(prodConfig);
}
}
五、开源框架中的应用
5.1 StringBuilder - JDK内置
StringBuilder是建造者模式最经典的应用。
java
/**
* StringBuilder示例
*/
public class StringBuilderDemo {
public static void main(String[] args) {
// StringBuilder使用建造者模式构建字符串
StringBuilder sb = new StringBuilder();
String result = sb.append("Hello")
.append(" ")
.append("World")
.append("!")
.insert(6, "Beautiful ")
.delete(0, 6)
.toString();
System.out.println(result); // Beautiful World!
// StringBuilder原理(简化)
/*
public StringBuilder append(String str) {
super.append(str);
return this; // 返回this实现链式调用
}
*/
}
}
5.2 Lombok @Builder
Lombok通过注解自动生成Builder代码。
java
/**
* 使用Lombok @Builder注解
*
* 需要添加Lombok依赖:
* <dependency>
* <groupId>org.projectlombok</groupId>
* <artifactId>lombok</artifactId>
* </dependency>
*/
// @Builder
// @Data
// @AllArgsConstructor(access = AccessLevel.PRIVATE)
class Employee {
private Long id;
private String name;
private String email;
private String department;
private Double salary;
private java.time.LocalDate hireDate;
// Lombok会自动生成:
// 1. Builder内部类
// 2. 所有字段的setter方法(返回Builder)
// 3. build()方法
}
/**
* 手动演示Lombok生成的代码
*/
class EmployeeManual {
private Long id;
private String name;
private String email;
private String department;
private Double salary;
private EmployeeManual(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.email = builder.email;
this.department = builder.department;
this.salary = builder.salary;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Long id;
private String name;
private String email;
private String department;
private Double salary;
public Builder id(Long id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder department(String department) {
this.department = department;
return this;
}
public Builder salary(Double salary) {
this.salary = salary;
return this;
}
public EmployeeManual build() {
return new EmployeeManual(this);
}
}
@Override
public String toString() {
return "Employee{id=" + id + ", name='" + name + "', email='" + email +
"', department='" + department + "', salary=" + salary + "}";
}
}
/**
* 使用示例
*/
class LombokBuilderDemo {
public static void main(String[] args) {
// 使用Builder创建对象
EmployeeManual employee = EmployeeManual.builder()
.id(1L)
.name("张三")
.email("zhangsan@example.com")
.department("技术部")
.salary(15000.0)
.build();
System.out.println(employee);
}
}
5.3 OkHttp Request.Builder
OkHttp的HTTP请求构建器。
java
/**
* OkHttp Request.Builder示例
*
* OkHttp是Square开源的HTTP客户端
*/
// 实际OkHttp用法:
/*
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://api.example.com/users")
.header("Authorization", "Bearer token123")
.addHeader("User-Agent", "MyApp/1.0")
.get()
.build();
Response response = client.newCall(request).execute();
*/
/**
* 简化的Request.Builder实现原理
*/
class SimpleRequest {
private final String url;
private final String method;
private final java.util.Map<String, String> headers;
private SimpleRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = new java.util.HashMap<>(builder.headers);
}
public static class Builder {
private String url;
private String method = "GET";
private java.util.Map<String, String> headers = new java.util.HashMap<>();
public Builder url(String url) {
this.url = url;
return this;
}
public Builder get() {
return method("GET");
}
public Builder post() {
return method("POST");
}
public Builder method(String method) {
this.method = method;
return this;
}
public Builder header(String name, String value) {
this.headers.put(name, value);
return this;
}
public Builder addHeader(String name, String value) {
// 与header区别:addHeader允许重复的header
return header(name, value);
}
public SimpleRequest build() {
if (url == null) {
throw new IllegalStateException("url == null");
}
return new SimpleRequest(this);
}
}
@Override
public String toString() {
return method + " " + url + "\nHeaders: " + headers;
}
}
/**
* 测试
*/
class OkHttpBuilderDemo {
public static void main(String[] args) {
SimpleRequest request = new SimpleRequest.Builder()
.url("https://api.example.com/users")
.get()
.header("Authorization", "Bearer token123")
.addHeader("User-Agent", "MyApp/1.0")
.build();
System.out.println(request);
}
}
5.4 Guava ImmutableList.Builder
Google Guava的不可变集合构建器。
java
/**
* Guava ImmutableList.Builder示例
*/
// 实际Guava用法:
/*
import com.google.common.collect.ImmutableList;
ImmutableList<String> list = ImmutableList.<String>builder()
.add("A")
.add("B")
.add("C")
.addAll(Arrays.asList("D", "E"))
.build();
*/
/**
* 简化的ImmutableList.Builder实现原理
*/
class SimpleImmutableList<E> {
private final java.util.List<E> elements;
private SimpleImmutableList(Builder<E> builder) {
this.elements = java.util.Collections.unmodifiableList(
new java.util.ArrayList<>(builder.elements)
);
}
public static <E> Builder<E> builder() {
return new Builder<>();
}
public static class Builder<E> {
private java.util.List<E> elements = new java.util.ArrayList<>();
public Builder<E> add(E element) {
elements.add(element);
return this;
}
public Builder<E> addAll(Iterable<? extends E> elements) {
for (E element : elements) {
this.elements.add(element);
}
return this;
}
public SimpleImmutableList<E> build() {
return new SimpleImmutableList<>(this);
}
}
public java.util.List<E> asList() {
return elements;
}
@Override
public String toString() {
return elements.toString();
}
}
/**
* 测试
*/
class GuavaBuilderDemo {
public static void main(String[] args) {
SimpleImmutableList<String> list = SimpleImmutableList.<String>builder()
.add("Apple")
.add("Banana")
.add("Cherry")
.addAll(java.util.Arrays.asList("Date", "Elderberry"))
.build();
System.out.println("不可变列表: " + list);
// 尝试修改会抛出异常
try {
list.asList().add("Fig");
} catch (UnsupportedOperationException e) {
System.out.println("不可变列表无法修改!");
}
}
}
六、建造者模式的优缺点
6.1 优点
1. 参数清晰,易于理解
// 传统构造函数
new User("张三", 25, "男", "北京", "138****", "email@example.com");
❌ 参数顺序难记,容易出错
// 建造者模式
User user = User.builder()
.name("张三")
.age(25)
.gender("男")
.city("北京")
.build();
✓ 参数名称明确,不会弄混
2. 支持链式调用,代码优雅
java
Computer computer = Computer.builder()
.cpu("Intel i7")
.ram("16GB")
.storage("512GB SSD")
.build();
3. 易于扩展,符合开闭原则
添加新参数:
只需在Builder中添加新方法
不影响现有代码
4. 可以创建不可变对象
java
// 字段声明为final
private final String name;
// 只在构造函数中赋值
private User(Builder builder) {
this.name = builder.name;
}
// 线程安全
5. 参数校验集中
java
public User build() {
// 集中校验
if (name == null) {
throw new IllegalArgumentException("姓名不能为空");
}
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄不合法");
}
return new User(this);
}
6.2 缺点
1. 代码量增加
需要编写Builder内部类
代码量约为原来的2倍
2. 性能开销
创建对象前需要先创建Builder
对性能敏感的场景需要考虑
3. 不适合简单对象
如果对象只有2-3个属性
使用建造者模式反而复杂
七、最佳实践
7.1 使用Lombok简化代码
java
/**
* 使用Lombok @Builder注解
*/
// @Data
// @Builder
// @AllArgsConstructor(access = AccessLevel.PRIVATE)
class Product {
private Long id;
private String name;
private Double price;
private String category;
}
// 一个注解搞定,无需手写Builder代码
7.2 必选参数处理
java
/**
* 方式1:通过Builder构造函数
*/
class User1 {
private final String name; // 必选
private final int age; // 必选
private String email; // 可选
private User1(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.email = builder.email;
}
public static class Builder {
private final String name;
private final int age;
private String email;
// 必选参数通过构造函数传入
public Builder(String name, int age) {
this.name = name;
this.age = age;
}
public Builder email(String email) {
this.email = email;
return this;
}
public User1 build() {
return new User1(this);
}
}
}
// 使用:必须提供必选参数
// User1 user = new User1.Builder("张三", 25)
// .email("zhangsan@example.com")
// .build();
/**
* 方式2:在build()中校验
*/
class User2 {
// 所有字段都通过方法设置
// 在build()中校验必选字段
}
7.3 支持默认值
java
public static class Builder {
private String cpu;
private String ram;
private String storage = "256GB SSD"; // 默认值
private String gpu = "集成显卡"; // 默认值
// ...
}
7.4 Builder复用
java
/**
* 可以从现有对象创建Builder
*/
class User {
// ...
public Builder toBuilder() {
return new Builder()
.name(this.name)
.age(this.age)
.email(this.email);
}
}
// 使用:复制并修改
/*
User user1 = User.builder()
.name("张三")
.age(25)
.build();
User user2 = user1.toBuilder()
.age(26) // 只修改年龄
.build();
*/
八、总结
8.1 核心要点
- 建造者模式的本质:将复杂对象的构建与表示分离
- 适用场景 :
- 对象参数很多(5个以上)
- 有多个可选参数
- 需要创建不可变对象
- 对象创建过程复杂
- 关键特征 :
- 链式调用
- 参数清晰
- 延迟构建
- 参数校验
8.2 使用建议
选择建造者模式的检查清单:
✓ 对象参数超过4个?
✓ 有可选参数?
✓ 参数类型相同容易混淆?
✓ 需要不可变对象?
✓ 对象创建过程复杂?
如果有2个以上"是",建议使用建造者模式!
8.3 与其他创建型模式的对比
建造者 vs 工厂方法:
- 建造者:关注如何组装对象
- 工厂方法:关注创建哪个对象
建造者 vs 抽象工厂:
- 建造者:一步步构建复杂对象
- 抽象工厂:创建一系列相关对象
建造者 vs 原型:
- 建造者:指定参数构建新对象
- 原型:克隆现有对象
8.4 实践经验
- 优先使用Lombok @Builder:减少模板代码
- 适度使用:简单对象不要用建造者模式
- 参数校验:在build()方法中集中校验
- 不可变对象:字段声明为final,构造后不可修改
- 命名规范:Builder方法名与字段名一致