【Effective Java】條1:考慮用靜態工廠方法代替構造器

在日常編程中,獲取類的實例通常採用構造器的方法。還有另一種方法叫作“靜態工廠方法(Static Factory Method)”。譬如Boolean類中這段代碼:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

使用靜態工廠方法優點

  1. 靜態工廠方法有名稱

    構造函數的方法名比較單調,只能與類名相同。特別是當有多個構造函數時,有可能不能很好的區分彼此之間的參數區別。而靜態方法爲普通的方法,名字可以依據方法的作用來設定。

    譬如類java.util.concurrent.Executors,其中的靜態方法如下:

    //創建一個包含固定數量線程的線程池
    public static ExecutorService newFixedThreadPool(int nThreads)
    //創建一個線程池,使線程能支持設定等級的併發
    public static ExecutorService newWorkStealingPool(int parallelism)
  2. 靜態工廠方法可以每次不必創建新對象

    每次調用構造函數都需要創建新對象。但是在靜態工廠方法中,則可以返回緩存的對象或者預先構建的對象。譬如最上面的public static Boolean valueOf(boolean b)代碼。

  3. 靜態工廠方法的返回類型可以爲子類型對象

    這種靈活性有很多好處。

    • 靜態工廠方法的返回對象的類可以不用是public的,這樣隱藏了類的實現細節

      大家可以查看Collections類的代碼,裏面提供了很多的靜態工廠方法,如:

      public class CollectionDemo {
      
        public static void main(String[] args) {
          //list1爲同步列表
          List<Object> list1 = Collections.synchronizedList(Lists.newArrayList());
          //list2爲不可改變的列表
          List<Object> list2 = Collections.unmodifiableList(Lists.newArrayList());
      
          //list1和list2雖都爲列表,但也不是同種列表,list1爲同步列表,list2則爲不可改變的列表
        }
      }
    • 靜態工廠方法的返回對象的類的實現是可以隨着版本迭代進行修改的

      在實際開發中大家都有迭代的痛苦,如果某個接口修改了實現也需要調用方進行修改,那是很痛苦的。這裏我們可以參考EnumSet

      查看EnumSet的代碼發現,其沒有開放的構造方法,僅提供許多靜態工廠方法,如下:

      方法 描述
      static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) 創建包含elementType所有元素的EnumSet對象
      EnumSet<E> clone() 返回當前EnumSet對象的複製結果
      static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) 創建包含elementType對象,且包含s中的所有元素
      static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) 創建EnumSet對象,且用c中的元素進行初始化
      static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) 創建EnumSet對象,且用s中的元素進行初始化
      static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) 用指定的類型創建空的EnumSet對象
      static <E extends Enum<E>> EnumSet<E> of(E e) 創建EnumSet對象,並用e初始化
      static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) 創建EnumSet對象,並用firstrest元素初始化
      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) 創建EnumSet對象,並用e1e2元素初始化
      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) 創建EnumSet對象,並用e1e2e3元素初始化
      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) 創建EnumSet對象,並用e1e2e3e4元素初始化
      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5) 創建EnumSet對象,並用e1e2e3e4e5元素初始化
      static <E extends Enum<E>> EnumSet<E> range(E from, E to) 創建EnumSet對象,並用fromto之間的元素初始化

      以EnumSet類型中的noneOf方法爲例說明,當elementType的元素個數小於64時,則調用RegularEnumSet,否則調用JumboEnumSet。當在以後的迭代中,RegularEnumSet的性能足以支撐超過64個元素時,我們完全可以刪除調用JumboEnumSet的實現,而不會影響調用方。

      public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> var0) {
          Enum[] var1 = getUniverse(var0);
          if (var1 == null) {
              throw new ClassCastException(var0 + " not an enum");
          } else {
              return (EnumSet)(var1.length <= 64 ? new RegularEnumSet(var0, var1) : new JumboEnumSet(var0, var1));
          }
      }
    • 靜態工廠方法的返回對象的類,在編寫的時候可以不必存在,也即服務提供者框架。

  4. 靜態工廠方法在創建參數化類型實例時,使代碼更簡潔。這在高版本的JDK中已經不存在。
    此處的意思是,之前在創建某些類時,譬如Map<String, List<String>> map = new HashMap<String, List<String>>(),片段String, List<String>需要重複寫兩次,在高版本JDK中已經可以不用這樣寫了,可以寫爲Map<String, List<String>> map = new HashMap()

使用靜態工廠缺點

  1. 類若不包含公有的或受保護的構造器,則不能被子類化
  2. 與其他靜態方法相比沒有區別。意思是指用戶可能不明白此靜態工廠方法是用來實例化對象的,沒有構造函數那麼直白。平時編程中,儘量採用易懂的方法命名來表示,如newInstancegetInstancegetTypenewTypevalueOfof這樣的命名
發佈了98 篇原創文章 · 獲贊 180 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章