建造者模式是得到类的实例的方法之一。它常常用于构建参数初始状态复杂的实例,例如有许多可选属性的类,从而可以避免提供过多的、琐碎的构造方法。例如,某个系统的用户类,它有两个必须的属性,若干个可选的属性。
public class User {
//required fields
private final String userId;
private String password;
//optional fields
private String name;
private String gender;
private String birthday;
private String email;
private String phone;
//Construstors
public User(String userId, String password){
this.userId = userId;
this.password = password;
}
public User(String userId, String password, String name){
this.userId = userId;
this.password = password;
this.name = name;
}
//...
}
为了避免提供过多的构造方法,我们首先可以采用 JavaBean 的模式思想,只提供一个初始化必须属性的构造方法,而其它可选属性使用 setter 进行初始化,如下所示。
public class User {
//required fields
private final String userId;
private String password;
//optional fields
private String name;
private String gender;
private String birthday;
private String email;
private String phone;
//Construstors
public User(String userId, String password){
this.userId = userId;
this.password = password;
}
//Setters
public void setName(String name){
this.name = name;
}
public void setGender(String gender){
this.gender = gender;
}
//...
}
JavaBean 的 Setter 很好地解决了实例初始化的问题,但其仍有缺点导致一些场景下无法满足需求。例如,在某些实名制系统中,要求姓名(name)、性别(gender)、出生日期(birthday) 属性一旦被初始化就不能改变,这时我们需要用 final 修饰相应的属性,这时,就无法为相应的属性添加 setter 了。
使用一个静态内部类作为类的建造者有点类似使用静态工厂方法产生一个类的实例。但是,和 JavaBean 类似,建造者将实例的构建过程分成若干个步骤,允许使用的时候初始化部分属性,从而可以建造不同的类实例。
//建造者接口
public interface Builder<T> {
public T build();
}
//产品类
public class User {
//required fields
private final String userId;
private String password;
//optional fields
private final String name;
private final String gender;
private final String birthday;
private String email;
private String phone;
private User(UserBuilder bulider){
this.userId = bulider.userId;
this.password = bulider.password;
this.name = bulider.name;
this.gender = bulider.gender;
this.birthday = bulider.birthday;
this.email = bulider.email;
this.phone = bulider.phone;
}
@Override
public String toString(){
return "User ID : " + this.userId + "\n"
+ "User name: " + this.name + "\n";
}
public static class UserBuilder implements Builder<User> {
//required fields
private final String userId;
private String password;
//optional fields
private String name;
private String gender;
private String birthday;
private String email;
private String phone;
public UserBuilder(String userId, String password){
this.userId = userId;
this.password = password;
}
public UserBuilder setName(String name){
this.name = name;
return this;
}
public UserBuilder setGender(String gender){
this.gender = gender;
return this;
}
public UserBuilder setBirthday(String birthday){
this.birthday = birthday;
return this;
}
public UserBuilder setEmail(String email){
this.email = email;
return this;
}
public UserBuilder setPhone(String phone){
this.phone = phone;
return this;
}
@Override
public User build() {
return new User(this);
}
}
}
使用泛型 Builder 接口,可以将建造者作为类工厂提供给需要使用的对象实例的地方。
import org.junit.Test;
public class UserTest {
private <T> void consumer(Builder<T> bulider){
T obj = builder.build();
System.out.println(obj);
}
@Test
public void test() {
Builder<User> userBuilder = new User.UserBuilder("00001", "simple_pw").setName("张三").setGender("男");
consumer(userBuilder);
}
}
//执行后输出为
//User ID : 00001
//User name: 张三
consumer 使用一个建造者 builder,然而它并不关心 builder 做了哪些初始化工作,得到后就可以调用 build 方法构建出它想要使用的对象即可。这样实际上分离了类的使用和类的构建过程。
如果我们研究的更深入一点,将 User 作为产品的公共接口,然后不同种类的 User 实现该接口后分别定义自己的 Builder,就能结合建造者模式和工厂模式实现更复杂的需求。