Builder模式
2018-12-14 本文已影响0人
Ray昱成
为何而来
假设有一个复杂对象example,里面有很多不可变属性(final)。实现方法通常有两种,一种是提供很多个构造方法用于实例化对象,另外一种是new一个对象,提供get/set方法。
两种方法的缺点:
- 构造方法多而且参数多,代码可读性就很差,并且难以维护
- 通过new对象然后set,在多线程或者其他情况下会产生对象不一致的状态。调用者看到了这个对象后,以为这个对象已经创建完毕,其实还没有具体初始化。
利用Builder模式,我们可以解决上面的问题
Builder模式例子
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
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(this);
}
}
}
//创建User对象
new User.UserBuilder("张", "三")
.age(20)
.phone("123456789")
.address("我在哪")
.build();
通常情况下的基本原则:
- User类的构造方法是私有的。也就是说调用者不能直接创建User对象
- 类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getters方法
- Builder是静态内部类,构造方法中只接收必传的参数,并且对应的成员变量是final
Builder模式优缺点
- 客户端的代码更容易写,使用了链式调用,可读性非常好
- 不会产生同一对象不一致的状态
- 会产生多余的Builder对象,消耗内存
线程安全与参数约束
User对象是不可变的,因此是线程安全的;但是,Builder对象并不具有线程安全性。因此,当我们需要对User对象的参数强加约束条件时,我们应该可以对builder()方法中所创建出来的User对象进行检验
public User build() {
User user = new user(this);
if (user.getAge() > 120) {
throw new IllegalStateException(“Age out of range”); // 线程安全
}
return user;
}
需要特别注意的是,我们是对Person对象进行参数检查,而不是对Builder对象进行参数检查,因为Builder对象不是线程安全的,即下面的代码存在线程安全问题:
public User build() {
if (age > 120) {
throw new IllegalStateException(“Age out of range”); // 非线程安全
}
return new User(this);
}
经典的Builder模式

其中:
- Product 产品抽象类。
- Builder 抽象的Builder类。
- ConcretBuilder 具体的Builder类。
- Director 同一组装过程
mybatis中Builder模式用法
