第2条:遇到多个构造器参数时要考虑用构建器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
假如一份营养成分的标签,有两个域是必须的,四个域是可选的。
1.重叠构造器模式
提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个构造器有两个可选参数,依次类推,直到最后一个构造器包含所有的可选参数。
缺点:谁作为第一个可选参数是一个问题,因为后一个包含两个可选参数的构造器必须传递值给第一个可选参数,导致需要设置许多本不想设置的参数,像是一个极端的例子NutritionFacts n = new NutritionFacts(240,8,0,0,0,10),只有最后一个可选参数是我们想设置的,然而之前的三个可选参数我们也不得不设置。
2.JavaBean模式
通过一个无参构造器创建对象,然后调用setter方法来设置每个必要的参数。
这样创建实例很容易,产生的代码读起来也比较容易,因为通过setter的方法名能知道设置的参数是哪一个。
缺点:构造过程被分到了几个调用之中,在构造过程中无法保证JavaBean的一致性。
3.Builder模式
不直接生成想要的对象,而是让客户利用所有必要的参数调用构造器,得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法设置每个可选参数,最后,客户端调用无参的build方法来生成不可变的对象。
public class NutritionFacts { private int servingSize;//required private int servings;//required private int calories;//optional private int fat;//optional private int sodium;//optional private int carbohyrate;//optional public static class Builder {//静态Builder类 private final int servingSize; private final int servings; private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohyrate = 0; public Builder(int servingSize, int servings) {//required参数必须在构造器中提供 this.servingSize = servingSize; this.servings = servings; }
//optional参数在类似setter的方法中赋值 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) { carbohyrate = val; return this; }
//无参的build方法来生成目标实例 public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) {//把Builder实例作为构造器参数,buidler会自动填充未赋值的可选域 servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohyrate = builder.carbohyrate; } }
实例化NutritionFacts:
NutritionFacts n = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
注意到我们没有给fat域赋值,Builder会自动帮我们填充默认值0
现在构造过程在一条语句上,没有JavaBean模式的缺点,同时具有JavaBean模式的可读性,又能避免重叠构造器模式为不想设置的参数不得不设置的缺点。
Builder模式的缺点:
为了创建对象,必须先创建它的构造器,在十分注重性能的情况下,可能就不太适用了。Builder模式比重叠构造器模式的代码更长,因此只有在很多参数的时候才使用,比如4个或者更多的参数。