构造函数和静态工厂方法1都不能很好的处理多参数问题。
譬如有一个类表示包装食品外面显示的营养成分标签,这些标签包含必须的成分:每份的含量,每罐的含量以及每份的卡路里,还有一些其他非必须的显示,譬如总脂肪量、饱和脂肪量、转化脂肪、胆固醇、钠等等。
public class NutritionFacts {
/** 必须参数*/
private int servingSize;
/** 必须参数*/
private int servings;
/** 非必须*/
private int calories;
/** 非必须*/
private int fat;
/** 非必须*/
private int sodium;
/** 非必须*/
private int carbohydrate;
}
试想一下,构建这个类的构造函数,采用构造器和静态工厂方法都不是很好理解。由于参数太多,你可能会分几个构造函数来构造(重叠构造器),又或者采用JavaBean来构建,又或者采用本文所说的构造器模式。
重叠构造器模式
首先提供个必须参数构成的构造函数,然后每增加一个非必须参数构成一个构造函数。实现如下:
public class NutritionFacts {
/** 必须参数*/
private int servingSize;
/** 必须参数*/
private int servings;
/** 非必须*/
private int calories;
/** 非必须*/
private int fat;
/** 非必须*/
private int sodium;
/** 非必须*/
private int carbohydrate;
public NutritionFacts(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public NutritionFacts(int servingSize, int servings, int calories) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
}
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;
}
}
缺点也显而易见,当参数足够多的时候,调用方可能需要参照构造函数将参数一一对应。万一构造函数改变了参数的顺序,调用方也不得不进行修改。
JavaBean
构造
提供默认的构造函数,然后采用set
方法进行设置。代码如下:
public class NutritionFacts {
/** 必须参数*/
private int servingSize;
/** 必须参数*/
private int servings;
/** 非必须*/
private int calories;
/** 非必须*/
private int fat;
/** 非必须*/
private int sodium;
/** 非必须*/
private int carbohydrate;
//Getter & Setter
}
public class NutritionFactsDemo {
public static void main(String[] args) {
NutritionFacts nutritionFacts = new NutritionFacts();
nutritionFacts.setServings(100);
nutritionFacts.setServingSize(10);
}
}
JavaBean
自身有严重的缺点。其一,由于对象的构造是分步进行的,导致Bean
可能不处于一致性状态;其二,在多线程环境中,开发者需要确保构造对象的线程安全。
构造模式(推荐)
不像前两种方式,构造模式先用必须的参数构建builder
对象,然后在builder
对象为非必须参数调用类似setter
的方法,最后调用无参的build
方法来生成对象。
public class NutritionFacts {
/** 必须参数*/
private int servingSize;
/** 必须参数*/
private int servings;
/** 非必须*/
private int calories;
/** 非必须*/
private int fat;
/** 非必须*/
private int sodium;
/** 非必须*/
private int carbohydrate;
private NutritionFacts(Builder builder) {
this.servingSize = builder.getServingSize();
this.servings = builder.getServings();
this.calories = builder.getCalories();
this.fat = builder.getFat();
this.sodium = builder.getSodium();
this.carbohydrate = builder.getCarbohydrate();
}
public static final class Builder {
/** 必须参数*/
private int servingSize;
/** 必须参数*/
private int servings;
private int calories;
private int fat;
private int sodium;
private int carbohydrate;
private Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public static Builder newInstance(int servingSize, int servings) {
return new Builder(servingSize, servings);
}
public Builder servingSize(int servingSize) {
this.servingSize = servingSize;
return this;
}
public Builder servings(int servings) {
this.servings = servings;
return this;
}
public Builder calories(int calories) {
this.calories = calories;
return this;
}
public Builder fat(int fat) {
this.fat = fat;
return this;
}
public Builder sodium(int sodium) {
this.sodium = sodium;
return this;
}
public Builder carbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
// Getter & Setter
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
}
构造器模式不是没有缺点,缺点就是多了很多代码。但是对于多参数的构造,特别是实际开发中,还是强烈建议使用构造器模式。
多啰嗦一句,在Intellij idea
中可以在插件2中搜索builder
,使用此插件可以直接生成构造器模式的代码。