静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。考虑用一个类表示包装食品外面显示的营养成分标签。这些标签中有几个域是必须的:每份的含量、每罐的含量以及每份的卡路里,还有超过20个可选域:总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。大多数产品在某几个可选域中都会有非零的值。
对于这样的类,应该用哪种构造器或者静态方法来编写呢?
1. 重叠构造器模式
- // Telescoping constructor pattern - does not scale well!
- 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;
- }
- public static void main(String[] args) {
- NutritionFacts cocaCola =
- new NutritionFacts(240, 8, 100, 0, 35, 27);
- }
- }
2. JavaBeans模式
// JavaBeans Pattern - allows inconsistency, mandates mutability 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; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); } }3. Builder模式,既能保证安全性,又能保证可读性。
- // Builder Pattern
- 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 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 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;
- }
- public static void main(String[] args) {
- NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
- calories(100).sodium(35).carbohydrate(27).build();
- }
- }
简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。与使用传统的重叠构造器模式相比,使用Builder模式的客户端代码将更易于阅读和编写,构造器也比JavaBeans更加安全。