天天看點

【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

      對象,并用

      first

      rest

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)

      建立

      EnumSet

      對象,并用

      e1

      e2

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)

      建立

      EnumSet

      對象,并用

      e1

      e2

      e3

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)

      建立

      EnumSet

      對象,并用

      e1

      e2

      e3

      e4

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)

      建立

      EnumSet

      對象,并用

      e1

      e2

      e3

      e4

      e5

      元素初始化

      static <E extends Enum<E>> EnumSet<E> range(E from, E to)

      建立

      EnumSet

      對象,并用

      from

      to

      之間的元素初始化
      以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 <=  ? 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. 與其他靜态方法相比沒有差別。意思是指使用者可能不明白此靜态工廠方法是用來執行個體化對象的,沒有構造函數那麼直白。平時程式設計中,盡量采用易懂的方法命名來表示,如

    newInstance

    getInstance

    getType

    newType

    valueOf

    of

    這樣的命名

繼續閱讀