構造函數和靜態工廠方法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
,使用此插件可以直接生成構造器模式的代碼。