静态工厂与构造器都有一个局限性:不能很好地扩展到大量的可选参数。
如果使用构造器来创建实例,便需要大量的构造方法,例如:
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方法来进行赋值:
public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() {} // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } }这样创建实例时的代码可读性就要相比于使用构造器就要高出许多,但是这样做就相当于阻止了把类做成不可变的可能,需要付出额外的努力来确保线程安全。
而使用构建器则可以保证JavaBean模式的可读性,也可以保证构造器模式的安全性:
public class NutritionFacts { 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; // Optional parameters - initialized to default values 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 cocaCola = new NutritionFacts.Builder(240, 8) .calories(100).sodium(35).carbohydrate(27).build()但是构建器模式的缺点也很明显: 1. 为了创建对象,需要先创建它的构建器,虽然开销很小,但是在一些很注重性能的情况下,这就是一个问题了。 2. 可以发现,构建器模式比构造器更为冗长,所以也只有在参数数量较多时才会考虑使用。