目录
- [1. 什么是建造者模式?](#1. 什么是建造者模式?)
- [2. 为什么需要建造者模式?](#2. 为什么需要建造者模式?)
- [3. 基础实现](#3. 基础实现)
- [4. 进阶实现](#4. 进阶实现)
- [5. 实际应用场景](#5. 实际应用场景)
- [6. 与其他方式的对比](#6. 与其他方式的对比)
- [7. 总结](#7. 总结)
1. 什么是建造者模式?
建造者模式是一种创建对象的方式。想象你要组装一台电脑:
- 传统方式:一次性告诉店员所有配置(CPU、内存、硬盘...),容易说错
- 建造者方式:一步步选择(先选CPU,再选内存,再选硬盘...),最后组装
1.1 核心思想
java
// 建造者模式的基本流程
对象 = 建造器
.设置属性A(值A)
.设置属性B(值B)
.设置属性C(值C)
.构建();
1.2 关键特点
- 分步骤构建:一个属性一个属性地设置
- 链式调用:可以连续调用方法
- 最终构建 :最后调用
build()方法创建对象 - 不可变对象:创建后对象不能修改
2. 为什么需要建造者模式?
2.1 解决的问题
问题1:构造函数参数太多
java
// ❌ 不好的方式:参数太多,容易搞错顺序
public class Computer {
public Computer(String cpu, String memory, String storage,
String graphics, String motherboard, boolean wifi,
boolean bluetooth, String powerSupply, String caseType) {
// 参数太多,容易传错
}
}
// 使用时容易出错
Computer computer = new Computer("Intel i7", "32GB", "1TB",
"RTX4070", "ASUS", true, false, "650W", "ATX");
// ↑ 这是什么意思?
问题2:可选参数难处理
java
// ❌ 不好的方式:需要很多重载的构造函数
public class Computer {
public Computer(String cpu, String memory) { ... }
public Computer(String cpu, String memory, String storage) { ... }
public Computer(String cpu, String memory, String storage, String graphics) { ... }
// 需要写很多个构造函数...
}
问题3:代码可读性差
java
// ❌ 看不出每个参数的含义
Computer computer = new Computer("Intel i7", "32GB", "1TB", "RTX4070", null, true);
2.2 建造者模式的解决方案
java
// ✅ 建造者模式:清晰、灵活、易读
Computer computer = new Computer.Builder()
.setCpu("Intel i7-12700K") // 清楚知道在设置CPU
.setMemory("32GB DDR4") // 清楚知道在设置内存
.setStorage("1TB NVMe SSD") // 清楚知道在设置存储
.setGraphics("RTX 4070") // 清楚知道在设置显卡
.setWifi(true) // 清楚知道在设置WiFi
.build(); // 最后构建对象
3. 基础实现
3.1 第一个建造者模式示例
让我们从一个简单的学生类开始。这个例子会完整展示建造者模式的所有核心要素,帮助你彻底理解这个模式的运作机制。
设计思路 :
我们要创建一个学生类,包含姓名、年龄、学校、专业、GPA等信息。如果用传统的构造函数,参数会很多且容易搞错顺序。使用建造者模式,我们可以让创建过程更清晰、更安全。
java
/**
* 学生类 - 使用建造者模式创建
*/
public class Student {
// 🔥 关键点1:所有字段都是final的,创建后不能修改
// 这样设计的好处:
// 1. 对象一旦创建就不可变,更安全
// 2. 多线程环境下不用担心数据被意外修改
// 3. 符合函数式编程的不可变对象思想
private final String name;
private final int age;
private final String school;
private final String major;
private final double gpa;
// 🔥 关键点2:私有构造器,只能通过Builder创建
// 为什么要设为私有?
// 1. 防止用户直接 new Student(),强制使用Builder
// 2. 确保所有Student对象都经过Builder的验证流程
// 3. 维护设计模式的完整性和一致性
private Student(StudentBuilder builder) {
// 🔥 关键点3:从Builder对象中提取数据
// 这里发生了数据的"转移":从可变的Builder转移到不可变的Student
// builder参数包含了用户通过链式调用设置的所有信息
this.name = builder.name;
this.age = builder.age;
this.school = builder.school;
this.major = builder.major;
this.gpa = builder.gpa;
}
// getter方法
public String getName() { return name; }
public int getAge() { return age; }
public String getSchool() { return school; }
public String getMajor() { return major; }
public double getGpa() { return gpa; }
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school='" + school + '\'' +
", major='" + major + '\'' +
", gpa=" + gpa +
'}';
}
/**
* 🔥 关键点4:建造者内部类
* 为什么要设计成静态内部类?
* 1. 静态内部类可以访问外部类的私有构造器
* 2. 不需要外部类的实例就能创建Builder(节省内存)
* 3. 逻辑上Builder属于Student的一部分,放在一起更合理
* 4. 用户使用时直接 new Student.StudentBuilder() 很直观
*/
public static class StudentBuilder {
// 🔥 关键点5:Builder的字段用来临时存储配置信息
// 注意:这些字段不是final的,因为需要通过setter方法修改
// 这些字段就像"收集篮",用来收集用户的配置选择
private String name; // 临时存储姓名
private int age; // 临时存储年龄
private String school; // 临时存储学校
private String major; // 临时存储专业
private double gpa; // 临时存储GPA
// 🔥 关键点6:链式设置方法(Fluent Interface)
// 这是建造者模式最重要的特性之一
public StudentBuilder setName(String name) {
// 第1步:将用户传入的参数保存到Builder的字段中
this.name = name;
// 第2步:返回当前Builder对象本身(this)
// 这一步是实现链式调用的关键!
// 返回this意味着用户可以继续在这个Builder上调用其他方法
return this;
}
// 🔥 所有setter方法都遵循相同的模式:
// 1. 保存参数到对应字段
// 2. 返回this支持链式调用
public StudentBuilder setAge(int age) {
this.age = age;
return this; // 继续支持链式调用
}
public StudentBuilder setSchool(String school) {
this.school = school;
return this; // 继续支持链式调用
}
public StudentBuilder setMajor(String major) {
this.major = major;
return this; // 继续支持链式调用
}
public StudentBuilder setGpa(double gpa) {
this.gpa = gpa;
return this; // 继续支持链式调用
}
// 🔥 关键点7:最终构建方法 - 建造者模式的核心
// 这个方法是整个建造者模式的"收官之作"
public Student build() {
// 🔥 第一阶段:数据验证
// 为什么要在build()中验证?
// 1. 集中验证:所有验证逻辑都在一个地方,便于维护
// 2. 延迟验证:用户可能会多次修改属性,最后验证更高效
// 3. 完整性检查:可以验证多个字段之间的关系
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("学生姓名不能为空");
}
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间");
}
// 可以添加更复杂的业务逻辑验证
// 比如:if (gpa > 4.0 && school.equals("某些学校")) { ... }
// 🔥 第二阶段:创建最终对象
// 调用Student的私有构造器,将当前Builder对象传递过去
// 此时发生"数据转移":从可变的Builder变成不可变的Student
return new Student(this);
// 注意:这里的this指向当前Builder对象,包含了所有用户设置的数据
}
}
}
3.2 如何使用
现在让我们看看如何使用刚才创建的Student建造者。这个使用示例会展示建造者模式在实际应用中的灵活性和便利性。
java
public class StudentExample {
public static void main(String[] args) {
// 🔥 示例1:完整配置的学生对象
// 这里演示了建造者模式的完整流程
Student student1 = new Student.StudentBuilder() // ←第1步:创建Builder对象
.setName("张三") // ←第2步:设置姓名,返回Builder
.setAge(20) // ←第3步:设置年龄,返回Builder
.setSchool("北京大学") // ←第4步:设置学校,返回Builder
.setMajor("计算机科学") // ←第5步:设置专业,返回Builder
.setGpa(3.8) // ←第6步:设置GPA,返回Builder
.build(); // ←第7步:调用build()创建Student对象
System.out.println("完整配置的学生:" + student1);
// 🔥 示例2:部分配置的学生对象
// 建造者模式的一个重要优势:不需要设置所有属性
// 可以根据实际需要选择设置哪些属性
Student student2 = new Student.StudentBuilder()
.setName("李四") // 只设置必需的基本信息
.setAge(19)
.setSchool("清华大学")
// 注意:这里没有设置major和gpa,它们会是默认值(null或0)
.build();
System.out.println("部分配置的学生:" + student2);
// 🔥 示例3:演示链式调用的本质
// 让我们分解链式调用,看看每一步都发生了什么
System.out.println("\n=== 分解链式调用过程 ===");
// 第1步:创建Builder
Student.StudentBuilder builder = new Student.StudentBuilder();
System.out.println("第1步:创建了Builder对象");
// 第2步:设置姓名
builder = builder.setName("王五");
System.out.println("第2步:设置姓名后,返回的还是同一个Builder对象");
// 第3步:设置年龄
builder = builder.setAge(21);
System.out.println("第3步:设置年龄后,继续返回Builder对象");
// 第4步:最终构建
Student student3 = builder.build();
System.out.println("第4步:调用build()创建最终的Student对象:" + student3);
// 🔥 示例4:演示验证机制
// 当我们提供不合法的数据时会发生什么?
try {
Student invalidStudent = new Student.StudentBuilder()
.setName("") // 空姓名(不合法)
.setAge(-5) // 负数年龄(不合法)
.build();
} catch (IllegalArgumentException e) {
System.out.println("\n验证失败:" + e.getMessage());
System.out.println("这证明了build()方法中的验证逻辑生效了!");
}
}
}
3.3 关键点深入解释
🔥 链式调用的工作原理详解
问题 :为什么每个setter方法都要返回this?
java
public StudentBuilder setName(String name) {
this.name = name;
return this; // 🔥 这一行是链式调用的核心!
}
答案:这是实现**流畅接口(Fluent Interface)**的关键技术。让我们深入理解:
java
// 🔥 内存层面的执行过程分析
Student.StudentBuilder builder = new Student.StudentBuilder();
// 此时内存中有一个Builder对象,假设地址是 @12345
builder = builder.setName("张三");
// setName方法执行:
// 1. this.name = "张三" (设置Builder对象@12345的name字段)
// 2. return this; (返回Builder对象@12345的引用)
// 结果:builder变量仍然指向同一个对象@12345
builder = builder.setAge(20);
// setAge方法执行:
// 1. this.age = 20 (设置Builder对象@12345的age字段)
// 2. return this; (返回Builder对象@12345的引用)
// 结果:builder变量仍然指向同一个对象@12345
// 🔥 关键洞察:始终是同一个Builder对象在被修改!
为什么可以链式调用?
java
// 原理分析:方法调用的返回值可以继续调用方法
new Student.StudentBuilder() // 创建Builder对象,返回Builder引用
.setName("张三") // 在Builder上调用setName,返回同一个Builder
.setAge(20) // 在返回的Builder上调用setAge,返回同一个Builder
.setSchool("北大") // 在返回的Builder上调用setSchool,返回同一个Builder
.build(); // 在返回的Builder上调用build,返回Student对象
// 等价于:
Student.StudentBuilder temp1 = new Student.StudentBuilder();
Student.StudentBuilder temp2 = temp1.setName("张三"); // temp2 == temp1 (同一个对象)
Student.StudentBuilder temp3 = temp2.setAge(20); // temp3 == temp1 (同一个对象)
Student.StudentBuilder temp4 = temp3.setSchool("北大"); // temp4 == temp1 (同一个对象)
Student finalStudent = temp4.build();
如果不返回this会怎样?
java
// ❌ 错误的设计:不返回this
public void setName(String name) { // 注意:返回类型是void
this.name = name;
// 没有return this;
}
// 使用时的问题:
Student.StudentBuilder builder = new Student.StudentBuilder();
builder.setName("张三"); // 这个方法没有返回值
.setAge(20); // ❌ 编译错误!void类型不能继续调用方法
🔥 为什么构造器必须是私有的?
设计原理 :私有构造器是建造者模式的强制约束机制。
java
private Student(StudentBuilder builder) {
// 🔥 私有构造器的三大作用:
// 1. 访问控制:只有同一个类内部可以调用
// 2. 强制使用Builder:用户无法绕过Builder直接创建对象
// 3. 保证一致性:所有Student对象都经过相同的创建流程
}
对比分析:
java
// ❌ 如果构造器是public的会发生什么?
public Student(String name, int age, String school, String major, double gpa) {
this.name = name;
this.age = age;
// ...
}
// 用户就可以绕过Builder:
Student student1 = new Student("张三", 20, "北大", "计算机", 3.8); // 直接构造
Student student2 = new Student.StudentBuilder().setName("李四").build(); // Builder方式
// 这样就有了两种创建方式,破坏了设计的一致性!
// 而且直接构造的方式没有经过Builder的验证逻辑
java
// ✅ 私有构造器确保唯一的创建路径
private Student(StudentBuilder builder) { /* ... */ }
// 用户只能这样创建:
Student student = new Student.StudentBuilder() // 必须使用Builder
.setName("张三")
.setAge(20)
.build(); // 经过验证
// new Student(...); // ❌ 编译错误:构造器不可见
🔥 为什么字段必须是final的?
设计原理 :final关键字创建真正不可变的对象。
java
private final String name; // 🔥 final的三重保障:
深入分析:
java
public class Student {
private final String name; // ✅ final字段
private final int age; // ✅ final字段
private Student(StudentBuilder builder) {
this.name = builder.name; // ✅ 构造器中一次性赋值
this.age = builder.age; // ✅ 之后再也不能修改
}
// ✅ 只有getter,没有setter
public String getName() {
return name;
}
// ❌ 如果有setter就破坏了不可变性
// public void setName(String name) { this.name = name; } // 编译错误!final字段不能重新赋值
}
不可变对象的优势:
java
// 🔥 优势1:线程安全
// 多个线程同时访问Student对象,不会有数据竞争问题
Student student = new Student.StudentBuilder().setName("张三").build();
Thread thread1 = new Thread(() -> {
String name = student.getName(); // ✅ 安全读取,不会被其他线程修改
});
Thread thread2 = new Thread(() -> {
String name = student.getName(); // ✅ 安全读取,得到的总是一致的数据
});
// 🔥 优势2:可以安全地共享和缓存
Map<String, Student> studentCache = new HashMap<>();
studentCache.put("张三", student); // ✅ 可以安全缓存,因为对象不会变化
// 🔥 优势3:作为Map的key是安全的
Map<Student, String> grades = new HashMap<>();
grades.put(student, "A+"); // ✅ Student对象不会变化,hashCode保持稳定
对比可变对象的问题:
java
// ❌ 如果Student是可变的:
public class MutableStudent {
private String name; // 不是final
public void setName(String name) { // 提供了setter
this.name = name;
}
}
// 使用时的问题:
MutableStudent student = new MutableStudent("张三");
Map<MutableStudent, String> grades = new HashMap<>();
grades.put(student, "A+");
student.setName("李四"); // ❌ 对象被修改了!
String grade = grades.get(student); // ❌ 可能返回null,因为hashCode变了
4. 进阶实现
4.1 带默认值的建造者
在实际项目中,我们经常需要为配置对象提供合理的默认值。这样用户只需要修改他们关心的部分,其他配置可以使用默认值,大大提高了易用性。
设计思路:
- 默认值策略:为每个字段提供生产环境中最常用的配置
- 覆盖机制:用户可以选择性地覆盖任何默认值
- 智能默认值:根据使用场景提供最合理的初始配置
java
/**
* 电脑配置类 - 带默认值的建造者
* 这个例子展示了如何在Builder中预设合理的默认值
*/
public class Computer {
private final String cpu;
private final String memory;
private final String storage;
private final String graphics;
private final boolean wifi;
private final boolean bluetooth;
private Computer(ComputerBuilder builder) {
// 🔥 数据转移:从Builder的临时存储转移到Computer的永久存储
// 这里转移的可能是用户自定义的值,也可能是Builder预设的默认值
this.cpu = builder.cpu;
this.memory = builder.memory;
this.storage = builder.storage;
this.graphics = builder.graphics;
this.wifi = builder.wifi;
this.bluetooth = builder.bluetooth;
}
// getter方法...
public String getCpu() { return cpu; }
public String getMemory() { return memory; }
public String getStorage() { return storage; }
public String getGraphics() { return graphics; }
public boolean hasWifi() { return wifi; }
public boolean hasBluetooth() { return bluetooth; }
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memory='" + memory + '\'' +
", storage='" + storage + '\'' +
", graphics='" + graphics + '\'' +
", wifi=" + wifi +
", bluetooth=" + bluetooth +
'}';
}
public static class ComputerBuilder {
// 🔥 关键设计:在字段声明时直接初始化默认值
// 这种方式的优势:
// 1. 用户可以直接调用build()而不设置任何参数,获得一个可用的默认配置
// 2. 默认值集中管理,便于维护和修改
// 3. 新手友好:即使不了解所有配置项也能获得合理的结果
private String cpu = "Intel Core i5"; // 🔥 中端CPU,适合大多数用户
private String memory = "8GB DDR4"; // 🔥 标准内存配置,满足日常需求
private String storage = "256GB SSD"; // 🔥 SSD提供更好的性能体验
private String graphics = "集成显卡"; // 🔥 集成显卡节省成本,适合办公
private boolean wifi = true; // 🔥 现代电脑必备功能
private boolean bluetooth = false; // 🔥 可选功能,默认关闭节省电源
public ComputerBuilder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public ComputerBuilder setMemory(String memory) {
this.memory = memory;
return this;
}
public ComputerBuilder setStorage(String storage) {
this.storage = storage;
return this;
}
public ComputerBuilder setGraphics(String graphics) {
this.graphics = graphics;
return this;
}
public ComputerBuilder setWifi(boolean wifi) {
this.wifi = wifi;
return this;
}
public ComputerBuilder setBluetooth(boolean bluetooth) {
this.bluetooth = bluetooth;
return this;
}
public Computer build() {
// 验证必需的配置
if (cpu == null || cpu.trim().isEmpty()) {
throw new IllegalArgumentException("CPU配置不能为空");
}
return new Computer(this);
}
}
}
4.2 使用默认值的Builder
java
public class ComputerExample {
public static void main(String[] args) {
// 使用默认配置,只修改需要的部分
Computer basicComputer = new Computer.ComputerBuilder()
.build(); // 直接使用默认值
System.out.println("基础配置:" + basicComputer);
// 高端配置,覆盖默认值
Computer gamingComputer = new Computer.ComputerBuilder()
.setCpu("Intel i7-12700K")
.setMemory("32GB DDR4")
.setStorage("1TB NVMe SSD")
.setGraphics("RTX 4070")
.setBluetooth(true)
.build();
System.out.println("游戏配置:" + gamingComputer);
// 办公配置
Computer officeComputer = new Computer.ComputerBuilder()
.setCpu("Intel i5-12400")
.setMemory("16GB DDR4")
.setStorage("512GB SSD")
.build(); // WiFi和蓝牙使用默认值
System.out.println("办公配置:" + officeComputer);
}
}
4.3 带验证的高级Builder
java
/**
* 用户注册信息 - 带完整验证的建造者
*/
public class User {
private final String username;
private final String email;
private final String password;
private final int age;
private final String phone;
private User(UserBuilder builder) {
this.username = builder.username;
this.email = builder.email;
this.password = builder.password;
this.age = builder.age;
this.phone = builder.phone;
}
// getter方法...
public static class UserBuilder {
private String username;
private String email;
private String password;
private int age;
private String phone;
public UserBuilder setUsername(String username) {
this.username = username;
return this;
}
public UserBuilder setEmail(String email) {
this.email = email;
return this;
}
public UserBuilder setPassword(String password) {
this.password = password;
return this;
}
public UserBuilder setAge(int age) {
this.age = age;
return this;
}
public UserBuilder setPhone(String phone) {
this.phone = phone;
return this;
}
public User build() {
// 详细的验证逻辑
validateUsername();
validateEmail();
validatePassword();
validateAge();
validatePhone();
return new User(this);
}
private void validateUsername() {
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (username.length() < 3 || username.length() > 20) {
throw new IllegalArgumentException("用户名长度必须在3-20个字符之间");
}
}
private void validateEmail() {
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
}
private void validatePassword() {
if (password == null || password.length() < 6) {
throw new IllegalArgumentException("密码长度至少6位");
}
}
private void validateAge() {
if (age < 13 || age > 120) {
throw new IllegalArgumentException("年龄必须在13-120之间");
}
}
private void validatePhone() {
if (phone != null && phone.length() != 11) {
throw new IllegalArgumentException("手机号码必须是11位");
}
}
}
}
5. 实际应用场景
5.1 HTTP请求构建
java
/**
* HTTP请求建造者 - 实际应用场景
*/
public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private final int timeout;
private HttpRequest(HttpRequestBuilder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = new HashMap<>(builder.headers);
this.body = builder.body;
this.timeout = builder.timeout;
}
// getter方法...
public static class HttpRequestBuilder {
private String url;
private String method = "GET"; // 默认GET请求
private Map<String, String> headers = new HashMap<>();
private String body;
private int timeout = 30000; // 默认30秒超时
public HttpRequestBuilder url(String url) {
this.url = url;
return this;
}
public HttpRequestBuilder method(String method) {
this.method = method;
return this;
}
public HttpRequestBuilder header(String name, String value) {
this.headers.put(name, value);
return this;
}
public HttpRequestBuilder headers(Map<String, String> headers) {
this.headers.putAll(headers);
return this;
}
public HttpRequestBuilder body(String body) {
this.body = body;
return this;
}
public HttpRequestBuilder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public HttpRequest build() {
if (url == null || url.trim().isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
// POST请求必须有body
if ("POST".equalsIgnoreCase(method) && body == null) {
throw new IllegalArgumentException("POST请求必须包含请求体");
}
return new HttpRequest(this);
}
}
}
使用示例:
java
public class HttpRequestExample {
public static void main(String[] args) {
// GET请求
HttpRequest getRequest = new HttpRequest.HttpRequestBuilder()
.url("https://api.example.com/users")
.header("Authorization", "Bearer token123")
.timeout(5000)
.build();
// POST请求
HttpRequest postRequest = new HttpRequest.HttpRequestBuilder()
.url("https://api.example.com/users")
.method("POST")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token123")
.body("{\"name\":\"张三\",\"age\":25}")
.build();
}
}
5.2 数据库连接配置
java
/**
* 数据库配置建造者
*/
public class DatabaseConfig {
private final String host;
private final int port;
private final String database;
private final String username;
private final String password;
private final int maxConnections;
private final int timeout;
private final boolean useSSL;
private DatabaseConfig(DatabaseConfigBuilder builder) {
this.host = builder.host;
this.port = builder.port;
this.database = builder.database;
this.username = builder.username;
this.password = builder.password;
this.maxConnections = builder.maxConnections;
this.timeout = builder.timeout;
this.useSSL = builder.useSSL;
}
public static class DatabaseConfigBuilder {
private String host = "localhost"; // 默认主机
private int port = 3306; // 默认MySQL端口
private String database; // 数据库名(必需)
private String username; // 用户名(必需)
private String password; // 密码(必需)
private int maxConnections = 10; // 默认最大连接数
private int timeout = 30; // 默认超时时间(秒)
private boolean useSSL = false; // 默认不使用SSL
public DatabaseConfigBuilder host(String host) {
this.host = host;
return this;
}
public DatabaseConfigBuilder port(int port) {
this.port = port;
return this;
}
public DatabaseConfigBuilder database(String database) {
this.database = database;
return this;
}
public DatabaseConfigBuilder username(String username) {
this.username = username;
return this;
}
public DatabaseConfigBuilder password(String password) {
this.password = password;
return this;
}
public DatabaseConfigBuilder maxConnections(int maxConnections) {
this.maxConnections = maxConnections;
return this;
}
public DatabaseConfigBuilder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public DatabaseConfigBuilder useSSL(boolean useSSL) {
this.useSSL = useSSL;
return this;
}
public DatabaseConfig build() {
// 验证必需的字段
if (database == null || database.trim().isEmpty()) {
throw new IllegalArgumentException("数据库名不能为空");
}
if (username == null || username.trim().isEmpty()) {
throw new IllegalArgumentException("用户名不能为空");
}
if (password == null) {
throw new IllegalArgumentException("密码不能为空");
}
// 验证端口号
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("端口号必须在1-65535之间");
}
return new DatabaseConfig(this);
}
}
}
6. 与其他方式的对比
6.1 VS 构造函数
java
// ❌ 构造函数方式
public class PersonBad {
private String name;
private int age;
private String email;
private String phone;
private String address;
// 参数太多,容易搞错
public PersonBad(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;
}
// 需要很多重载构造函数
public PersonBad(String name, int age) { ... }
public PersonBad(String name, int age, String email) { ... }
public PersonBad(String name, int age, String email, String phone) { ... }
}
// 使用时容易出错
PersonBad person = new PersonBad("张三", 25, "123456789", "zhang@example.com", "北京");
// ↑ 这两个参数顺序搞错了!
// ✅ 建造者方式
PersonGood person = new PersonGood.Builder()
.setName("张三")
.setAge(25)
.setEmail("zhang@example.com") // 清楚知道在设置什么
.setPhone("123456789")
.setAddress("北京")
.build();
6.2 VS Setter方法
java
// ❌ Setter方式
public class PersonSetter {
private String name;
private int age;
private String email;
// 提供setter方法
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
public void setEmail(String email) { this.email = email; }
}
// 使用时的问题
PersonSetter person = new PersonSetter();
person.setName("张三");
person.setAge(25);
// 忘记设置email了!
// 而且对象创建后还可以被修改,不安全
// 在其他地方
person.setAge(30); // 对象被意外修改了!
// ✅ 建造者方式
PersonGood person = new PersonGood.Builder()
.setName("张三")
.setAge(25)
.setEmail("zhang@example.com")
.build(); // 创建后就不能修改了,更安全
6.3 VS 工厂模式
java
// 工厂模式:用于创建不同类型的对象
public class ComputerFactory {
public static Computer createGamingComputer() {
return new Computer("Intel i7", "32GB", "RTX 4070");
}
public static Computer createOfficeComputer() {
return new Computer("Intel i5", "16GB", "集成显卡");
}
}
// 建造者模式:用于灵活配置同一类型的对象
Computer computer = new Computer.Builder()
.setCpu("Intel i7") // 可以自由组合配置
.setMemory("32GB")
.setGraphics("RTX 4070")
.build();
7. 总结
7.1 建造者模式的优点
- 代码可读性强:每个方法名都说明了在设置什么
- 参数验证集中 :所有验证逻辑都在
build()方法中 - 灵活性高:可以自由组合不同的配置
- 不可变对象:创建后对象不能修改,更安全
- 默认值支持:可以为可选参数提供合理的默认值
7.2 适用场景
建造者模式特别适合以下情况:
- 参数很多(通常超过4个)
- 有可选参数
- 需要验证参数的组合
- 希望创建不可变对象
- 配置类、请求类、复杂实体类
7.3 实现要点
- 私有构造器:确保只能通过Builder创建对象
- 静态内部Builder类:方便使用,且能访问外部类的私有构造器
- 链式调用 :每个setter方法都返回
this - final字段:确保对象不可变
- build()方法验证:在创建对象前进行完整性检查
7.4 注意事项
- 代码量增加:需要写更多的代码
- 内存开销:Builder对象需要额外的内存
- 简单对象不适用:参数很少的对象直接用构造器更简单
7.5 实践建议
- 方法命名统一 :统一使用
setXxx()或withXxx() - 验证逻辑合理 :在
build()方法中进行必要的验证 - 提供默认值:为可选参数设置合理的默认值
- 文档完善:为Builder的每个方法写清楚注释
建造者模式是一个非常实用的设计模式,特别适合创建复杂的配置对象。掌握了它,你的代码会变得更清晰、更安全、更易维护