遇到多个构造器参数时要考虑用构建器
考虑用一个类表示包装食品外面显示的营养成分标签,这些标签中有几个域是必需的:每份的含量,每罐的含量以及每份的卡路里,还有一些可选域:钠,歆,铁,总脂肪含量等等!下面以四个可选域举例。
一般大家习惯采用重叠构造器模式:
1 public class NutritionFacts1 { 2 3 private final int servingSize; 4 private final int servings; 5 private final int calories; 6 private final int fat; 7 private final int sodium; 8 private final int carbohydrate; 9 10 public NutritionFacts1(int servingSize,int servings){ 11 this(servingSize,servings,0); 12 } 13 14 public NutritionFacts1(int servingSize,int servings,int calories){ 15 this(servingSize,servings,calories,0); 16 } 17 18 public NutritionFacts1(int servingSize,int servings,int calories,int fat){ 19 this(servingSize,servings,calories,fat,0); 20 } 21 22 public NutritionFacts1(int servingSize,int servings,int calories,int fat,int sodium){ 23 this(servingSize,servings,calories,fat,sodium,0); 24 } 25 26 public NutritionFacts1(int servingSize,int servings, 27 int calories,int fat,int sodium,int carbohydrate){ 28 this.servingSize=servingSize; 29 this.servings=servings; 30 this.calories=calories; 31 this.fat=fat; 32 this.sodium=sodium; 33 this.carbohydrate=carbohydrate; 34 } 35 36 }
1 public class BuilderTest { 2 3 public static void main(String[] args) { 4 5 NutritionFacts1 nf1 = new NutritionFacts1(240,8,100,0,35,27); 6 System.out.println(nf1.toString()); 7 8 } 9 10 }
这仅仅是6个参数,一旦参数增加,问题就来了!客户端代码会很难写,并且比较难以阅读!
第二种方法是JavaBeans模式:
1 public class NutritionFacts2 { 2 3 private int servingSize = -1; 4 private int servings = -1; 5 private int calories = 0; 6 private int fat = 0; 7 private int sodium = 0; 8 private int carbohydrate = 0; 9 10 public NutritionFacts2(){} 11 12 public void setServingSize(int servingSize){ 13 this.servingSize = servingSize; 14 } 15 public void setServings(int servings){ 16 this.servings = servings; 17 } 18 public void setCalories(int calories){ 19 this.calories = calories; 20 } 21 public void setFat(int fat){ 22 this.fat = fat; 23 } 24 public void setSodium(int sodium){ 25 this.sodium = sodium; 26 } 27 public void setCarbohydrate(int carbohydrate){ 28 this.carbohydrate = carbohydrate; 29 } 30 31 }
1 public class BuilderTest { 2 3 public static void main(String[] args) { 4 5 NutritionFacts2 nf2 = new NutritionFacts2(); 6 nf2.setServingSize(10); 7 nf2.setServings(20); 8 nf2.setCalories(1); 9 nf2.setCarbohydrate(5); 10 nf2.setFat(0); 11 nf2.setSodium(10); 12 13 } 14 15 }
JavaBeans模式的缺点:
1,构造的过程分到了几个调用中,在构造JavaBeans的时候可能会不一致。
2,类无法仅仅通过检验构造器参数的有效性来保证一致性!
3,对象的不一致会导致失败,JavaBeans模式阻止了把类做为不可变的可能,需要程序员做额外努力来保证它线程安全。
但是大部分的时候JavaBean是在线程中new出来的,只会供单线程使用,很少会出现多个线程共享JavaBean的情况!
第三种方法Builder模式,既保证了像重叠构造器模式那样的安全,也能保证像JavaBeans模式那么好的可读性!
1 public class NutritionFacts { 2 3 private final int servingSize; 4 private final int servings; 5 private final int calories; 6 private final int fat; 7 private final int sodium; 8 private final int carbohydrate; 9 10 public static class Builder { 11 private int servingSize; 12 private int servings; 13 14 private int calories = 0; 15 private int fat = 0; 16 private int sodium = 0; 17 private int carbohydrate = 0; 18 19 public Builder(int servingSize,int servings){ 20 this.servingSize=servingSize; 21 this.servings=servings; 22 } 23 24 public Builder calories(int val){ 25 calories=val; 26 return this; 27 } 28 public Builder fat(int val){ 29 fat=val; 30 return this; 31 } 32 public Builder sodium(int val){ 33 sodium=val; 34 return this; 35 } 36 public Builder carbohydrate(int val){ 37 carbohydrate=val; 38 return this; 39 } 40 41 public NutritionFacts build(){ 42 return new NutritionFacts(this); 43 } 44 } 45 46 private NutritionFacts(Builder builder){ 47 servingSize = builder.servingSize; 48 servings = builder.servings; 49 calories = builder.calories; 50 fat = builder.fat; 51 sodium = builder.sodium; 52 carbohydrate = builder.carbohydrate; 53 54 } 55 56 }
1 public class BuilderTest { 2 3 public static void main(String[] args) { 4 5 NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).carbohydrate(4).sodium(5).carbohydrate(6).build(); 6 7 } 8 9 }