本文定位于理解和總結(jié)<Effective Java>的所講內(nèi)容,而不是翻譯驮樊,因此不當(dāng)之處薇正,還請廣大網(wǎng)友指出。
復(fù)雜對象在這里指的是有三個以上(不包括三個)的屬性的對象囚衔。當(dāng)一個類具有很多屬性時挖腰,使用構(gòu)造器或者靜態(tài)工廠的方式創(chuàng)建新對象時不可避免的需要對很多屬性進行賦值,可維護性與可讀性都很低练湿。
通常使用重疊構(gòu)造器(telescoping constructor)或者JavaBean模式來構(gòu)建新對象(兩種模式具體代碼實例可參見<Effective Java>)猴仑,可以提高代碼可維護性和可讀性,然而這兩種方法并不完美肥哎。
重疊構(gòu)造器模式
重疊構(gòu)造器模式是指類有一系列的構(gòu)造器辽俗,構(gòu)造器的入?yún)膬H包含必選參數(shù)依次遞增到包含所有參數(shù),且必選參數(shù)構(gòu)造器通過逐級調(diào)用后續(xù)構(gòu)造器來實現(xiàn)篡诽。
實際上崖飘,重疊構(gòu)造器模式并沒有解決任何問題,當(dāng)可變參數(shù)增多時杈女,客戶端代碼依然難寫又難讀朱浴,并且擴展性極差吊圾,每增加一個屬性都要修改很多級的構(gòu)造器。
JavaBean模式
JavaBean模式是指先通過無參的構(gòu)造器來創(chuàng)建對象翰蠢,然后通過setter填充屬性的方法來構(gòu)建新對象项乒。
相對于重疊構(gòu)造器模式來說,JavaBean模式可讀性梁沧,可維護性檀何,可擴展性都很高,但是JavaBean模式本身存在嚴重的缺陷廷支。
對象在構(gòu)建過程中存在不一致性的可能
由于對象的構(gòu)建分多次調(diào)用才能完成频鉴,在構(gòu)建完成之前,使用對象可能會引起運行時錯誤酥泞,編譯期無法察覺砚殿。對象是可變的
由于使用setter來構(gòu)建對象啃憎,導(dǎo)致對象本身就失去了成為不可變對象的可能芝囤。需要依賴程序員來保證線程安全
不一致性導(dǎo)致需要程序員額外的工作來保證線程安全。
構(gòu)建者模式
除了以上兩種方式以外辛萍,還可以使用構(gòu)建者模式來創(chuàng)建對象悯姊,構(gòu)建者模式通過吸收必選參數(shù)和鏈式配置可選參數(shù)來完成對象的構(gòu)建。一般情況下贩毕,構(gòu)建者模式可實現(xiàn)為構(gòu)建對象的內(nèi)部類悯许。如下:
// 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;
}
}
在創(chuàng)建對象之前,需要先創(chuàng)建該對象構(gòu)建者的對象辉阶,并提供必選參數(shù)先壕,隨后鏈式配置可選參數(shù),調(diào)用構(gòu)建方法來完成對象的構(gòu)建谆甜,如下:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
構(gòu)建者模式融合了以上兩種方式的優(yōu)點垃僚,同時也避免了缺點,既保證了可讀性和可維護性又提高了線程安全规辱。
因此當(dāng)遇到有三個以上屬性的復(fù)雜對象構(gòu)建谆棺,尤其是有一些屬性是可選屬性時,不妨考慮使用構(gòu)建者模式罕袋。