靜态工廠方法相對構造器的優勢:
- 靜态工廠方法有名稱。一個類隻有一個帶有指定簽名的構造器。由于靜态工廠方法有名稱,是以不受限制。當一個類需要多個帶有相同簽名的構造器時,就用靜态工廠方法代替構造器,并選擇合适名稱突出差別。
- 靜态工廠方法不必在每次調用的時候都建立一個新對象。靜态工廠方法能為重複的調用傳回相同的對象,有助于類能控制在某個時刻哪些執行個體應該存在。[這種類稱為執行個體控制的類]
- 靜态工廠方法可以傳回原傳回類型的任何子類型的對象。
public interface Service {
//........
}
public interface Provider {
Service newService();
}
public class Services {
private Services(){}
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<Jenny>";
public static void registerDefultProvider(Provider p){
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
public static Service newInstance(){
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name){
Provider p = providers.get(name);
if(p == null){
throw new IllegalArgumentException("no provider registered with name: "+name);
}
return p.newService();
}
}
- 靜态工廠方法在建立參數化執行個體的時候使代碼變得更簡潔。但是,在調用參數化類的構造器時,即使類型參數很明顯,也必須指明。這需要兩次提供類型參數:
Map<String,List<String>> m = new HashMap<String,List<String>>();
//eg:HashMap提供了這個靜态工廠方法
public static <k, v> HashMap<k, v> newInstance(){
return new HashMap<k, v>();
}
//就可以通過以下代碼替換上面繁瑣的聲明
Map<String,List<String>> m = HashMap.newInstance();
靜态工廠方法的缺點:
- 類如果不含有公有的或受保護的構造器,就不能被子類化。
- 與其他的靜态方法沒有差別。
遇到多個構造器參數時,要用建構器
eg.多個構造器
public class NutritrionFats {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public NutritrionFats(int servingSize, int servings){
this(servingSize, servings, );
}
public NutritrionFats(int servingSize, int servings, int calories){
this(servingSize, servings, calories, );
}
public NutritrionFats(int servingSize, int servings, int calories, int fat){
this(servingSize, servings, calories, fat, );
}
public NutritrionFats(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;
}
}
JavaBean 建構器
public class NutritrionFats {
private int servingSize = -;
private int servings = -;
private int calories = ;
private int fat = ;
private int sodium = ;
public NutritrionFats(){ }
public void setServingSize(int val){ servingSize = val; }
public void setServings(int val){ servings = val; }
public void setCalories(int val){ calories = val; }
public void setFat(int val){ fat = val; }
public void setSodium(int val){ sodium = val; }
}
這種方式雖然彌補了重疊構造器的不足,但是,JavaBean自身存在不足,因構造過程被分到幾個調用中,在構造過程中JavaBean可能處于不一緻的狀态。類無法通過構造參數的有效性來保證一緻性。
下面是Builder模式來完善以上:
public class NutritrionFats {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public static class Builder{
private final int servingSize;
private final int servings;
private int calories = ;
private int fat = ;
private int sodium = ;
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 sodium(int val){
sodium = val;
return this;
}
public NutritrionFats build(){
return new NutritrionFats(this);
}
}
private NutritrionFats (Builder build){
servingSize = build.servingSize;
servings = build.servings;
calories = build.calories;
fat = build.fat;
sodium = build.sodium;
}
}
注意:NutritrionFats是不可變的,所有的預設參數都放在一個地方,builder的setter方法傳回builder本身,以便把調用連接配接起來。以下是調用用戶端的代碼: