建造者模式
建造者模式解决的是构建大型复杂的类对象的问题.
当我们定义了一个简单的类, 构造函数非常简单, 直接new即可
但是当构建参数非常多, 比如有几十上百个, 且并不是所有参数都能用得到, 我们实际用到的仅仅是将某一部分参数初始化的类对象, 此时这个构建的过程将会非常的复杂繁琐
为了解决这种通过不同的参数组合实例化不同的类的问题, 就有了建造者模式
建造者模式的实现方式非常多, 但核心思想是将
XX obj = new XXClass(param1, param2, ... ...)
这个实例化过程拆解为:
XX obj = new Builder().a().b().build()
这样使用一个构建器创建实例的过程
建造者模式本质上就是把"对象的构建过程"从"对象本身"拆出来,由一个 Builder 专门负责构建,再由 build() 返回最终对象
把构造函数拆成一步步可读、可控、可组合的链式调用。
以下是一个最精简的建造者模式实现:
java
public class BuilderMode {
public static void main(String[] args) {
Product product1 = new Product.Builder().b(5).build();
product1.run();
Product product2 = new Product.Builder().b(10).c(15).build();
product2.run();
}
}
class Product {
private int a = 12; // 默认固定参数
private int b;
private int c;
public void run() {
System.out.print("Product a = " + a);
if (b != 0) {
System.out.print(" b = " + b);
}
if (c != 0) {
System.out.print(" c = " + c);
}
System.out.println("");
}
// ***构建器***
// 构建器采用内部类的方式实现
// 这样可以将封装性做的很好, 避免Product类对外暴露属性
public static class Builder {
int b = 0;
int c = 0;
Builder b(int b) {
this.b = b;
return this;
}
Builder c(int c) {
this.c = c;
return this;
}
Product build() {
// 参数合法性验证及异常处理
// 处理异常过程省略
// 构建最终合法可运行的对象
Product product = new Product();
product.b = b;
product.c = c;
return product;
}
}
}
运行结果:
Product a = 12 b = 5
Product a = 12 b = 10 c = 15
下面是一个符合 Effective Java 推荐风格、贴近生产环境的建造者模式实现,涵盖空值校验、不可变性、链式调用、默认值、Builder 内部类、流畅 API 等核心特性的建造者模式类实现:
java
final class User {
// 核心字段(不可变 + 私有final)
private final Long id; // 必填:用户ID
private final String username; // 必填:用户名
private final String email; // 必填:邮箱
private final Integer age; // 可选:年龄(默认18)
private final String phone; // 可选:手机号(默认null)
private final List<String> roles; // 可选:角色列表(可变类型,需防御性拷贝)
private final LocalDateTime createTime; // 可选:创建时间(默认当前时间)
// 私有构造器:仅内部Builder可调用
private User(Builder builder) {
// 1. 必填参数校验(Effective Java强调:尽早校验参数)
this.id = Objects.requireNonNull(builder.id, "用户ID(id)不能为空");
this.username = Objects.requireNonNull(builder.username, "用户名(username)不能为空");
this.email = Objects.requireNonNull(builder.email, "邮箱(email)不能为空");
// 2. 可选参数(带默认值)
this.age = builder.age != null ? builder.age : 18;
this.phone = builder.phone;
this.createTime = builder.createTime != null ? builder.createTime : LocalDateTime.now();
// 3. 可变类型防御性拷贝(避免外部修改内部状态)
this.roles = builder.roles != null
? Collections.unmodifiableList(new ArrayList<>(builder.roles)) // 不可变列表
: Collections.emptyList();
}
// 静态内部Builder类(Effective Java推荐:静态内部类形式)
public static class Builder {
// 与User完全一致的字段(非final,用于临时存储)
private Long id;
private String username;
private String email;
private Integer age;
private String phone;
private List<String> roles;
private LocalDateTime createTime;
// 链式setter:返回Builder本身,支持流畅调用
public Builder id(Long id) {
this.id = id;
return this;
}
public Builder username(String username) {
this.username = username;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder age(Integer age) {
// 字段合法性校验(非空+业务规则)
if (age != null && (age < 0 || age > 150)) {
throw new IllegalArgumentException("年龄(age)必须在0-150之间");
}
this.age = age;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder roles(List<String> roles) {
this.roles = roles;
return this;
}
public Builder createTime(LocalDateTime createTime) {
this.createTime = createTime;
return this;
}
// 核心build方法:创建并返回不可变User对象
public User build() {
return new User(this);
}
}
// 所有字段的getter(无setter,保证不可变性)
public Long getId() {
return id;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
public Integer getAge() {
return age;
}
public String getPhone() {
return phone;
}
// 返回不可变视图,防止外部修改
public List<String> getRoles() {
return roles;
}
public LocalDateTime getCreateTime() {
return createTime;
}
// 重写equals/hashCode(生产环境必备)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(username, user.username) &&
Objects.equals(email, user.email) &&
Objects.equals(age, user.age) &&
Objects.equals(phone, user.phone) &&
Objects.equals(roles, user.roles) &&
Objects.equals(createTime, user.createTime);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email, age, phone, roles, createTime);
}
// 重写toString(调试/日志必备)
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
", roles=" + roles +
", createTime=" + createTime +
'}';
}
}
- 参数验证分布在
builder的每个字段设置时, 便于及时发现问题 - User类的所有属性都是final private权限, 只能通过内部构造器赋值
- 在build阶段校验参数非空等业务逻辑, 保证最终生成的对象正确可运行
- 构造器对默认值进行处理, 保证对象合法可用
- 对可变类型(如 List、Date)做拷贝,避免外部修改
测试:
java
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Collections;
import java.util.ArrayList;
public class BuilderMode {
public static void main(String[] args) {
// 1. 构建完整参数的User
User fullUser = new User.Builder()
.id(1001L)
.username("zhangsan")
.email("zhangsan@example.com")
.age(28)
.phone("13800138000")
.roles(List.of("admin", "user"))
.createTime(LocalDateTime.of(2025, 1, 1, 10, 0))
.build();
System.out.println("完整参数User: " + fullUser);
// 2. 构建仅必填参数的User(使用默认值)
User minimalUser = new User.Builder()
.id(1002L)
.username("lisi")
.email("lisi@example.com")
.build();
System.out.println("仅必填参数User: " + minimalUser);
// 3. 测试参数校验(年龄非法)
try {
new User.Builder()
.id(1003L)
.username("wangwu")
.email("wangwu@example.com")
.age(200) // 非法年龄
.build();
} catch (IllegalArgumentException e) {
System.out.println("参数校验生效: " + e.getMessage());
}
// 4. 测试不可变性(尝试修改roles)
try {
fullUser.getRoles().add("guest"); // 不可变列表会抛异常
} catch (UnsupportedOperationException e) {
System.out.println("不可变性校验生效: " + e.getMessage());
}
}
}
运行结果:
完整参数User: User{id=1001, username='zhangsan', email='zhangsan@example.com', age=28, phone='13800138000', roles=[admin, user], createTime=2025-01-01T10:00}
仅必填参数User: User{id=1002, username='lisi', email='lisi@example.com', age=18, phone='null', roles=[], createTime=2025-11-30T13:14:48.996069700}
参数校验生效: 年龄(age)必须在0-150之间
不可变性校验生效: null
关于GoF经典的建造和模式
经典建造者模式有如下几个角色组成:
Builder 抽象接口, 用来制定构建器的标准接口, 当有多个构建器时才会用到
ConcreteBuilder 构建器的具体实现, 生成实际的产品对象
Director 指挥家, 用来固定构建器的构建流程
Product 产品类, 包含最终所有的属性和方法, 但是只能由构建器实例化, 外部不能实例化
Client 调用端, 先获取构造器实例, 然后通过构造器创建产品实例对象.
经典建造者模式在实际生产过程中很少用到, 不再举例.