builder模式的新学习
静态工厂和构造器有个共同的局限性:他们不能很好的扩展到大量的可选参数。大多数产品在牧歌可选与中都会有非零的值
对于这种类,应该使用哪种构造器或者静态方法来进行编写?程序员一般习惯采用重叠构造器(telescoping constructor)模式。在这种模式下,你可以第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,以此类推,最后一个构造器包含所有的可选参数。
public class NutritionFacts { private final int servingSize; //required private final int servings; //required private final int calories; //optional private final int fat; //optional private final int sodium; //optional private final int carbohydrate; //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; }}
当我们想要穿件实例的时候,就利用参数列表最短的构造器,但是该列表包含了要设置的所有参数:
NutritionFacts cocaCloa = new NutritionFacts(240,8,100,0,35,27);
这个构造器通常需要许多你本不想设置的参数,但是不能不为它传递值。在这个例子中,我们给fat传递了一个值为0。如果“仅仅”只有6个参数,看起来并不算太早,问题是随着参数数目的增加,它很快就会失去控制。
使用JavaBean模式,也能够代替这种问题。遗憾的是在一定的情况下,我们需要额外的努力来保证它的安全性。
幸运的是,还有新的替代方法,既能够保证向重叠构造器模式那样安全,也能够保证像JavaBean模式的那么好的可读性。这就是Builder模式的一种形式。不直接生成想要的对象。然后客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setting的方法,来设置每个相关的可选参数。最后,客户端调用午餐的build方法来生成不可变的对象。这个builder是他构建的类的静态成员类。
//Builder Patternpublic class NutritionFacts{ private final int servingSize; //required private final int servings; //required private final int calories; //optional private final int fat; //optional private final int sodium; //optional private final int carbohydrate; //optional public static class Builder{ private final int servingSize; //required private final int servings; //required private int calories; //optional private int fat; //optional private int sodium; //optional private int carbohydrate; //optional public Builder(int servingSize,int servings){ this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val){ this.calories = val; return this; } public Builder fat(int val){ this.fat = val; return this; } public Builder sodium(int val){ this.sodium = val; return this; } public Builder carbohydrate(int val){ this.carbohydrate = val; 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.sodium = builder.sodium; this.carbohydrate = builder.carbohydrate; }}
注意NutrtionFacts是不可变的,所有的默认参数都是单独放在一个地方。builder的setter方法返回builder本身,以便可以把调用连接起来。
NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8) .calories(100) .sodium(35) .carbohydrate(27) .build();
这样的调用客户端代码很容易编写,更为重要的是易于阅读。
build方法可以检验这些约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对他们进行检验。如果违反了约束条件,build方法就应该抛出IllegalStateException。
Builder 模式自身的不足。为了创建对象,必须先创建它的构造器。虽然创建构造器的开销在实践中可能并不明显,但是在某些十分注重性能的情况下,可能就成问题了。
Builder 模式还比重叠构造器模式更加冗长,因此它只能在有很多参数的时候才使用。比如4个或者更多的参数。但是记住,将来你可能添加参数。如果一开始就是用构造器或者静态工厂,等类需要更多的参数时才添加构造器就会无法控制,那些过时的构造器或者静态工厂显得十分的不协调。因此,通常最好一开始就是用构造器。
简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类的时,Builder模式就是种不错的选择,特别是当大多数的参数都是可选的时候。