Effective Java 學習筆記(1)

通常情況下,我們在寫一個類時,一般會賦予它一個或幾個public的構造函數,讓外部程序能夠創建對象. 然而,在effective java 中,從另一個角度告誡我們在使用public的構造函數前,先考慮用public 的靜態函數來創建對象. 

 

其優點如下:

 

1. 一個靜態的工廠函數,可以取不同的名字. 而構造函數的名字只能與類同名. 

 

  一個API的設計中,一個好懂的名字可以免去使用者在文檔中查找其使用方法的麻煩. 

   如: 假如有個類叫Human,假如它有個構造函數 Human( int sex) ,根據參數來創建不同的對象. 用戶就可能去查文檔,sex=1 or sex=0 分別代表什麼. 但如果我們給它加兩個靜態的工廠函數,Human.getMan() 和 Human.getWoman() 用戶一看名字就知道是怎麼用.

   另外,正因爲靜態工廠函數可以取不同的名字,可以免去構造函數限定參數的煩惱.

   例子同上,如我們要有一個根據身高來創建一個Human對象,於是我們寫有Human(int height), 後來,我們又想根據年紀來創建一個對象,於是我們又有Human(int age), 很明顯,由於函數簽名的限制,我們只能選擇其中一個構造函數.但使用靜態工廠方法就沒有這個煩惱. 我們可以有Human.getInstanceByHeight(int height) 也可以有 Human.getInstanceByAge(int age),他們可以很合諧的在一起工作.

 

2. 使用靜態工廠函數,不必像構造函數那樣每次都創建對象,可以在很大程度上節約資源,特別是有些類僅有有限個數的對象時.

    

如 我們有Month這麼一個類, 按照原來的方法,我們可能有Month(int mon), 於是,每次調用一個構造函數,就會創建一個Month對象,其結果是內存中很可能存在大於12個Month對象. 然而,假如,我們事先定義12個靜態對象,從Jan到Dec,然後給一個Month.getMonth(int mon)這麼一個靜態工廠方法, 在函數中根據數字返回其中一個靜態對象,這樣,不管程序怎麼運行,內存中始終只有12個Month對象. 

 

這樣做還有一個好處就是, 用戶可以直接使用== 比較兩個對象是否相等,而不用equal方法.

 

3. 一個靜態工廠函數,返回的對象可以是這個類的對象,也可以是其子類的對象,增加了設計的靈活性。在實際運用中,可以得到幾個好處。

 

  其一,可以隱藏一些類,減少對外公佈的API數,精簡接口。這句話可以這樣理解。比如設計一個框架,裏面包含有20個類,但實際上,我們可以通過使用靜態工廠函數隱藏一些內部實現,使用戶不用知道全部的類及使用方法,使框架易用。書中的例子是Java的集合類,實際上sun實現了多達32個集合類,包括長度不可變的,或是線程安全的等等,這些類名是隱藏的,但我們不用直接面對這些複雜類名,只需要調用靜態方法就可以得到我們所需要的包含某個特性的集合類,使得這些集合方便易用。

 

其二,可能根據參數不同創建不同的對象。書中例子很好理解。設計了兩個對外接口都一樣的集合類,一個適合於小數量容量的集合,一個適合於大容量的集合。採用這種方式,用戶可以不用關心什麼情況下用什麼類,只要將長度參數傳入靜態工廠函數,這個靜態工廠函數就可以根據數據來判斷使用哪個類而返回這個類的對象。

 

此外,這種方式有很好的擴展性。我們可以在寫好方法後,再根據需要補充新的類。書中的例子寫得很好,這裏就把例子再簡化一下。

 

public interface Service {
      //這裏是一個服務。

      public doOperation();

}

 

/**

 * 這個類是來產生前一個類的

*/

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 = "Default Service";  //提供一個默認方法


// 註冊默認的服務提供商
public static void registerDefaultProvider(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();  //返回正確的對象
}
}

 

 

 4. 採用靜態工廠函數可以避免冗長的構造參數.

 

書上的例子說明的很清楚.

原來:

 

Map<String, List<String>> m = new HashMap<String, List<String>>();

 

現在:

 

public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}

 

Map<String, List<String>> m = HashMap.newInstance();

 

當然,什麼都是雙刃劍,有利則有弊.

 

缺點: 1. 當使用私有構造函數後, 類不能被繼承

        2. 當一個類包括多個靜態方法時, 靜態的工廠方法不易被區分開.

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章