静态工厂方法和构造器都有一个共同的特点–>无法扩展到大量的参数。
对于大量的参数类,我们有以下方案:
1.重叠构造器。这个源码中经常可以看到,例如HashMap:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
也就是说每个HashMap调用的其实都是含有全部参数的构造器public HashMap(int initialCapacity, float loadFactor)
这种重叠构造器的方法在参数较少的时候,是一种不错的选择,可以参数多了的话呢?
参数顺序容易混淆吧?每个参数的意义容易难看懂吧?冗余的代码写得太多了吧?
于是方案二出现了
2.javaBean模式
就是把这个类声明成javabean,只需要调用他的无参构造方法,set,set,set就好了。
这样就避免了很多重叠构造器的问题
但是问题是,set,set,set的连续使用让javabean的构造过程分开来了,让javabean处于不一致的状态(状态就是javabean的属性集合,相邻两个set方法过后的javabean属性不一样,也就是状态不一致),因此,有些不小心的程序员,在set,set,set方法之间使用了javabean,因此可能会导致错误。
有没有更好的方案呢?
3.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*类的构造方法是私有的。也就是说调用者不能直接创建User对象。
*User*类的属性都是不可变的。所有的属性都添加了final修饰符,因此User是不可变的,并且在构造方法中设置了值。并且,对外只提供getters方法。
Builder模式使用了链式调用。可读性更佳。
Builder的内部类构造方法中只接收必传的参数,并且该必传的参数适用了final修饰符,可选参数没有final修饰符,因此可以用此builder修改。
调用builder构建User:
new User.UserBuilder("王", "小二")
.age(20)
.phone("123456789")
.address("亚特兰蒂斯大陆")
.build();
Builder模式好处和优点:
使用Builder模式必然会导致写两遍相关属性的代码和SETTER方法,看起来有点吃力不讨好。
然而需要看到的是,客户端代码的可用性和可读性得到了大大提高。与此同时,构造函数的参数数量明显减少调用起来非常直观。
用Builder的构造函数而不是set方法传递客户需要的属性。这样做的好处在于,对象总是能被一次完整的实例化,这能避免对象不一致状态带来可能的错误。