在类中,静态工厂和构造器有一个共同的局限性:它们不能很好的扩展到大量的可选参数。
例如:当构造器的参数可能有1,2,···,8,···等,且构造器的个数不唯一。
(1)重叠构造器模式
在这种情况下开发者习惯采用重叠构造器模式,即提供只有一个参数的构造器,提供只有两个参数的构造,以此类推,最后一个构造器含有所有的可选参数。这种方式可行,但是当许多参数时,客户端代码会很难编写,并且阅读性很差。
(2)JavaBeans模式
在这种模式下,调用一个无惨构造函数来创建对象,然后调用setter方法来设置每个必要的参数,以及可选参数。这种模式弥补了重叠构造器模式的不足,创建实例容易,且代码阅读性较高,但是JavaBeans模式自身有很严重的缺点,当构造过程被分为几个部分完成时,可能会产生不一致的状态,类无法通过检验构造器的参数来保证一致性。第二个缺点在于无法将类变成不可变类,这就需要开发者付出额外的努力来保证线程安全。
(3)Builder模式
该模式可以保证向重叠构造器那样的安全性,又可以保证像JavaBeans模式那样的可读性。 不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后客户端调用无参的builder方法来生成不可变对象。注:这里的builder是它构建的类的静态成员类。如下案例:
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories = 0;
private final int fat = 0;
private final int sodium = 0;
private final int carbohydrate = 0;
public static class Builder {
//必填参数
private final int servingSize;
private final int servings;
//可选参数 - 默认参数值初始化
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize,int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
注:这里的NutritionFacts 是不可变的,所有的不可变参数都单独放在一个位置,builder的setter的方法返回builder本身,以便于链式调用。调用代码如下:
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();