本文定位于理解和總結<Effective Java>的所講內容,而不是翻譯,因此不當之處,還請廣大網友指出。
復雜對象在這里指的是有三個以上(不包括三個)的屬性的對象。當一個類具有很多屬性時,使用構造器或者靜態工廠的方式創建新對象時不可避免的需要對很多屬性進行賦值,可維護性與可讀性都很低。
通常使用重疊構造器(telescoping constructor)或者JavaBean模式來構建新對象(兩種模式具體代碼實例可參見<Effective Java>),可以提高代碼可維護性和可讀性,然而這兩種方法并不完美。
重疊構造器模式
重疊構造器模式是指類有一系列的構造器,構造器的入參從僅包含必選參數依次遞增到包含所有參數,且必選參數構造器通過逐級調用后續構造器來實現。
實際上,重疊構造器模式并沒有解決任何問題,當可變參數增多時,客戶端代碼依然難寫又難讀,并且擴展性極差,每增加一個屬性都要修改很多級的構造器。
JavaBean模式
JavaBean模式是指先通過無參的構造器來創建對象,然后通過setter填充屬性的方法來構建新對象。
相對于重疊構造器模式來說,JavaBean模式可讀性,可維護性,可擴展性都很高,但是JavaBean模式本身存在嚴重的缺陷。
對象在構建過程中存在不一致性的可能
由于對象的構建分多次調用才能完成,在構建完成之前,使用對象可能會引起運行時錯誤,編譯期無法察覺。對象是可變的
由于使用setter來構建對象,導致對象本身就失去了成為不可變對象的可能。需要依賴程序員來保證線程安全
不一致性導致需要程序員額外的工作來保證線程安全。
構建者模式
除了以上兩種方式以外,還可以使用構建者模式來創建對象,構建者模式通過吸收必選參數和鏈式配置可選參數來完成對象的構建。一般情況下,構建者模式可實現為構建對象的內部類。如下:
// 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
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;
}
}
在創建對象之前,需要先創建該對象構建者的對象,并提供必選參數,隨后鏈式配置可選參數,調用構建方法來完成對象的構建,如下:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
構建者模式融合了以上兩種方式的優點,同時也避免了缺點,既保證了可讀性和可維護性又提高了線程安全。
因此當遇到有三個以上屬性的復雜對象構建,尤其是有一些屬性是可選屬性時,不妨考慮使用構建者模式。