引入:
考虑有这样一个类的设计,它用来表示包装食品外面的营养标签,包含以下内容:
每罐的含量(servingSize)
营养品的种类(servings)
卡路里(calories)
脂肪含量(fat)
钠含量(sodium)
糖含量(carbohydrate)。
其中前两个是必须的,后面几项是可选的。
对于这样的类,应该用哪种构造方法或者静态方法来编写呢?一种常用的方法是采用重叠构造器(telescoping constructor)。例如:
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;
}
这种方法可行,但是当有许多参数时,客户端代码会很难编写,并且较难阅读。如果读者想明白这些值是什么意思,需要仔细的数着参数来探个究竟。一长串类型相似的参数会导致一些微妙的错误,如果客户端不小心颠倒了其中两个参数的顺序,编译器不会报错,但在程序运行时会出现错误的行为。
方案二
调用一个无参的构造方法来创建对象,然后调用setter方法来设置每个必要的参数,以及每个必要的参数。
public class NutritionFacts {
private int servingSize = -1;
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;}
pubic void setSodium(int val) {sodium = val;}
public void setCarbohydrate(int val){carbohydrate = val;}
}
这种模式弥补了重叠构造器模式的不足。就是创建实例容易,这样产生的代码也容易阅读。
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setcarbohydrate(27);
优缺点:
因为构造过程被分到了几个调用中,类的对象可能处于不一致的状态。类无法通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,而且,这种模式阻止了把类做成不可变的可能。
方案三(Builder模式)
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;
pubic static class Builder{
private final int servingSize;
private final int servings;
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
pubic 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 carbohydrate(int val) {
this.carbohydrate = val;
return this;
}
public Builder sodium(int val) {
this.sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
//私有Constructor表明创建NutritionFacts对象时,只能通过Builder对象的build()方法。
private NutritionFacts(Builder builder) {
serviceSize = builder.serviceSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
//test case.
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35)
.carbohydrate(27).build();
}
}
Builder模式可以有多个可变参数,使用单独的方法来设置每个参数。简言之,如果类的Constructor或静态工厂方法中有多个参数,就可以考虑使用Builder模式。