方法一是采用重叠构造器模式,缺点很明显,当参数很多时,需要的构造器要非常多,而且代码不易阅读。
方法二是采用JavaBeans模式,调用无参构造器来构造对象,再通过大量的set方法来设置参数,解决了方法一的缺点,但是构造过程被分到几个调用中,构造过程中JavaBean可能处于不一致的状态,需要额外的努力来确保它的线程安全(解决方法:当对象的构造完成,不允许在解冻之前使用,通过手动“冻结”对象,但是比较笨拙)。
方法三是采用Builder模式的一种形式,保证了安全性和可读性。
示例如下:
public class NutritionFacts {
private Integer servingSize;
private Integer servings;
private Integer calories;
private Integer fat;
private Integer sodium;
private Integer carbohydrate;
public static class Builder {
// Required parameters
private Integer servingSize;
private Integer servings;
// Optional parameters
private Integer calories;
private Integer fat = 0; // 添加一个默认值
private Integer carbohydrate;
private Integer sodium;
public Builder(Integer servingSize, Integer servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(Integer calories) {
this.calories = calories;
return this;
}
public Builder fat(Integer fat) {
this.fat = fat;
return this;
}
public Builder carbohydrate(Integer carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
public Builder sodium(Integer sodium) {
this.sodium = sodium;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.carbohydrate = builder.carbohydrate;
this.sodium = builder.sodium;
}
@Override
public String toString() {
return "NutritionFacts [servingSize=" + servingSize
+ ", servings=" + servings + ", calories=" + calories
+ ", fat=" + fat + ", sodium=" + sodium
+ ", carbohydrate=" + carbohydrate + "]";
}
}
public class Test {
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();
System.out.println("cocaCola: " + cocaCola);
NutritionFacts pepsiCola = new NutritionFacts.Builder(240, 8)
.calories(100)
.fat(35)
.carbohydrate(27)
.build();
System.out.println("pepsiCola: " + pepsiCola);
}
}
----------------------------------------------------
输出:
cocaCola: NutritionFacts [servingSize=240, servings=8, calories=100, fat=0, sodium=35, carbohydrate=27]
pepsiCola: NutritionFacts [servingSize=240, servings=8, calories=100, fat=35, sodium=null, carbohydrate=27]
如果使用JDK1.5及以上版本,只要一个泛型就能满足所有的builder,无论它们在构建哪种类型的对象。
// A builder for objects of type T
public interface Builder<T> {
T build();
}
public class NutritionFacts {
private Integer servingSize;
private Integer servings;
private Integer calories;
private Integer fat;
private Integer sodium;
private Integer carbohydrate;
public static class Builder implements com.sun.istack.internal.Builder<NutritionFacts> {
// Required parameters
private Integer servingSize;
private Integer servings;
// Optional parameters
private Integer calories;
private Integer fat = 0; // 添加一个默认值
private Integer carbohydrate;
private Integer sodium;
public Builder(Integer servingSize, Integer servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(Integer calories) {
this.calories = calories;
return this;
}
public Builder fat(Integer fat) {
this.fat = fat;
return this;
}
public Builder carbohydrate(Integer carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
public Builder sodium(Integer sodium) {
this.sodium = sodium;
return this;
}
@Override
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.carbohydrate = builder.carbohydrate;
this.sodium = builder.sodium;
}
@Override
public String toString() {
return "NutritionFacts [servingSize=" + servingSize
+ ", servings=" + servings + ", calories=" + calories
+ ", fat=" + fat + ", sodium=" + sodium
+ ", carbohydrate=" + carbohydrate + "]";
}
}
Builder模式也有不足,为了创建对象,必须先创建它的构建器,在某些十分注重性能的情况下,可能开销就有问题。Builder模式比重叠构造器模式更冗长。