静态工厂和构造器有个共同的局限性:它们都不能很好的扩展到大量的可选参数。
对于这样的类,应该是用哪种构造器或者静态方法来编写呢?
重构构造器
程序员一向习惯采用重叠构造器(telescoping constructor) 模式,在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依次类推,最后一个构造器包含所有的可选参数。
package 重叠构造器; public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (g) optional private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } }
缺点:重构构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且会很难阅读,如果客户端不小心颠倒了其中两个的顺序,编译器也不会出错,但是程序运行时会出现错误的行为。
JavaBeans模式
在这种模式下,调用一个无参构造器来创建对象,然后调用setter方法来设置每个必要的参数,以及每个相关的可选参数。
package 重叠构造器; public class NutritionFacts1 { private int servingSize = -1; private int servings = -1; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public void setServingSize(int servingSize) { this.servingSize = servingSize; } public void setServings(int servings) { this.servings = servings; } public void setCalories(int calories) { this.calories = calories; } public void setFat(int fat) { this.fat = fat; } public void setSodium(int sodium) { this.sodium = sodium; } public void setCarbohydrate(int carbohydrate) { this.carbohydrate = carbohydrate; } public NutritionFacts1() { } }缺点: 在构造过程中javabean可能处于不一致的状态
javabean模式阻止了把类做成不可变的可能(需要额外的努力来保证线程安全)
采用Builder模式
不直接生成对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。额庵后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可改变的对象。这个builder是它构建的类的静态成员类。下面就是它的实例:
package 重叠构造器; public class NutritionFacts3 { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; //一个内部类 //以后维护的时候,添加新参数的时候也很方便 public static class Builder { // Required parameters private final int servingSize; private final int servings; private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 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 carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts3 build() { return new NutritionFacts3(this); } } //构造函数需要传builder //可以使用单个builder构建多个对象 private NutritionFacts3(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } public static void main(String[] args) { //具有安全性和可读性 //build的时候才会验证参数是否正确 NutritionFacts3 cocaCola = new NutritionFacts3.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build(); } }
builder模式的不足:Builder模式比重叠构造器模式还要冗长,因此很多参数的时候才用它。