建造者模式(Builder pattern)通过将一个复杂对象的构建过程与它的表现分离,使得构建的过程可以自由扩展,降低部件与组装过程的耦合,是创建型模式。
考虑这样一个场景,假如有一个类(User),里面有很多属性,并且你希望这些类的属性都是不可变的(final),就像下面的代码。
public class User {
private final String firstName; // 必传参数
private final String lastName; // 必传参数
private final int age; // 可选参数
private final String phone; // 可选参数
private final String address; // 可选参数
}
在这个类中,有些参数是必要的,而有些参数是非必要的,就好比在注册用户时,用户的姓和名是必填的,而年龄、手机号和家庭地址等是非必需要。那么问题就来了,如何创建这个类的对象呢?
一种方案是使用构造方法。第一个构造方法只包含两个必需要参数,第二个构造方法中,增加一个可选参数,第三个构造方法中再增加一个可选参数,依次类推,直到构造方法中包含了所有参数。
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, "");
}
public User(String firstName, String lastName, int age, String phone) {
this(firstName, lastName, age, phone, "");
}
public User(String firstName, String lastName, int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = phone;
this.address = address;
}
弊端:
(1)一旦参数多了,代码可读性就差,并且难以维护。
(2)对调用者来说麻烦。
第二种解决方案,就是设置一个空参数的构造方法,然后为每一个属性设置setters和getters。
public class User {
private String firstName; // 必传参数
private String lastName; // 必传参数
private int age; // 可选参数
private String phone; // 可选参数
private String address; // 可选参数
public User() {
}
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;
}
}
弊端:
(1)不可变类的所有好处都不复存在。
(2)对象会产生不一致的状态,当你想要传入5个参数的时候,你必须将所有的setxx方法调用完成之后才行,然而一部分的调用者看到这个对象后,以为这个对象已创建完毕,就直接使用了,其实User对象并没有创建完成。
现在我们使用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);
}
}
}
有现个重要的地方需要强调一下:
(1)User类的构造方法是私有的,也就是说调用者不能直接创建User对象。
(2)User类的属性都是不可变的,所有的属性都添加了final修饰符,并且在构造方法中设置了值,并且,对外只提供getters方法。
(3)Builder模式使用了链式调用,可读性更佳。
(4)Builder的内部类构造方法中只接收必传的参数,并且该必传的参数使用final修饰符。
之前实例中的Builder模式,是省略掉了装饰者模式,这样结构更加简单,所以在很多开源框架源码中,大多都不是经典GOF的Builder模式,而省略后的。