1. 程式人生 > >Java中用靜態工廠方法代替構造器的優缺點

Java中用靜態工廠方法代替構造器的優缺點

Effective Java學習筆記,靜態工廠方法的善用。

一般情況下,對於類而言,我們獲取一個類的例項時,最常用的方法是提供一個公有的構造器。 但是還有一種方法,類可以提供一個公有的靜態工廠方法,它只是個返回類的例項的靜態方法而已。但是靜態工廠方法卻又許多妙用之處。 注意:類的靜態工廠方法與設計模式中的工廠方法不同。 Android原始碼中打量使用了靜態工廠方法。例如:
//Watchdog.java
public static Watchdog getInstance() {
        if (sWatchdog == null) {
            sWatchdog = new Watchdog();
        }
        return sWatchdog;
    }
為什麼Android原始碼中會打量使用靜態工廠方法呢?靜態工廠方法與構造器相比有哪些優勢呢? 1)它們有名稱。客戶端呼叫的時候見名知意,而且易於閱讀。比如getInstance方法,使用者呼叫的時候見名知意,就知道呼叫該方法可以獲得其所在類的一個例項。當一個類有多個相同簽名的構造器的時候,就用靜態工廠方法代替構造器,避免了使用者不知道該呼叫哪個構造器的問題。 2)不必每次呼叫它們的時候都建立一個新物件。尤其是在單例項類中使用非常廣泛,比如上面列舉的Watchdog.java中的getIns()方法,sWatchdog物件實際上只建立了一次,其他需要用Watchdog類的例項的地方,只需要呼叫getInstance()方法一次,就可以獲取該物件,簡單方便。 3)可以返回原返回型別的任何子型別物件。這個主要根據傳入工廠靜態方法的引數來實現,只要是已聲名的返回型別的子型別,都是可以的。這為我們選擇返回物件的型別提供了靈活性。 例如Android原始碼中ArraySet.java中的方法toArray,newInstance中的引數array.getClass().getComponentType() 即確定了new出來的物件是什麼型別的。 所有的JAVA類都繼承自object,那陣列也不例外,陣列的資料的型別可以是int或者String等,由getComponentType()來決定。這樣就體現了上邊說的靈活性,返回物件的型別由array決定。
 @Override
    public <T> T[] toArray(T[] array) {
        if (array.length < mSize) {
            @SuppressWarnings("unchecked") T[] newArray
                = (T[]) Array.newInstance(<span style="color:#3333ff;background-color: rgb(255, 255, 255);">array.getClass().getComponentType(), mSize</span>);
            array = newArray;
        }
        System.arraycopy(mArray, 0, array, 0, mSize);
        if (array.length > mSize) {
            array[mSize] = null;
        }
        return array;
    }
4)建立引數化型別例項的時候,使得程式碼更加簡潔。 呼叫引數化構造器的時候,通常要兩次提供型別引數:
Map<String, List<String>> m = new HashMap<String, List<String>>();
使用靜態工廠方法後,情況就不一樣了:
public static <K, V>HashMap<K, V> newInstance( ) {
return new HashMap<K, V>();
}
於是上邊建立m例項的的程式碼,就變成了下面這樣:
Map<String, List<String>> m = HashMap.newInstance();
是不是感覺簡潔了許多呢?newInstance()只需要寫一次,其他地方就可以直接呼叫了。 任何事物都有其兩面性,靜態工廠方法當然也有其缺點: 1)類如果不含公有的或者受保護的構造器,就不能被子類化。什麼意思呢?就是說如果類沒有public或者protected修飾的構造器,那麼就不能被另外一個類所繼承,也就是所謂的子類化。 2)它們與其他的靜態公有方法實際上沒有任何區別。 在API文件中,它們沒有像構造器那樣在API文件中被明確標識出來,因此,對於提供了靜態工廠方法而不是構造器的類來說,要想查明如何例項化一個類,是非常困難的。 下面是靜態工廠方法的一些慣用名稱:
  • valueOf 該方法返回的例項與引數具有相同的值,實際是型別轉換方法。
  • of  valueOf的一種替代,EnumSet。
  • getInstance Singleton單例模式,返回唯一的例項。
  • newInstance 返回的例項是一個新的例項,與之前返回的都不相同。
  • getType 與getInstance一樣,但是在工廠方法處於不同的類時候使用。
  • newType 與getInstance一樣,但是在工廠方法處於不同的類時候使用。
總之,靜態工廠方法和公有構造器各有優缺點,我們應該充分認識它們。但是靜態工廠方法通常更合適一些,千萬不要在建立物件時第一反應就是提供公有的構造器。這破壞了封裝性。