1. 程式人生 > >Effective Java 第三版閱讀筆記——條款1.考慮使用靜態工廠方法替代構造器

Effective Java 第三版閱讀筆記——條款1.考慮使用靜態工廠方法替代構造器

獲取一個類的例項的傳統方法是使用公開的構造器,除此之外,一個類還可以提供公開的靜態工廠方法(static factory method)來返回它的例項。例如 Boolean 類中的 valueOf 方法,這個方法將基本型別 boolean 轉換為一個 Boolean 物件的引用:

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

:靜態工廠方法與設計模式中的工廠方法不同。

靜態工廠方法的優點:

  • 與構造器不同,靜態工廠方法擁有自己的名字。考慮一個場景: BigInterger

    類的一個構造器 BigInterger(int, int, Random) 可能返回一個素數,然而傳統的構造器不能很直觀地表達出來,我們可以用一個靜態工廠方法 BigInteger.probablePrime 使表達變得更清晰。

    此外,如果一個類需要幾個引數型別相同的構造器,它只能打亂引數的順序來進行區別,這使得類的構造變得十分困難。此時可以考慮使用靜態工廠方法,這些方法擁有相同的引數型別,但你可以賦予它們精心挑選的名字,使它們的區別變得明顯。

  • 當靜態工廠方法被呼叫時,它們不需要每一次都建立一個新的物件。如開篇的 Boolean.valueOf(boolean) 所示,這個方法從來不建立物件,它只是返回 Boolean

    類中已經建立的物件:

    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
    

    這有效地提升了程式的效能。這一技巧與設計模式中的享元模式(Flyweight)十分相似。

  • 靜態工廠方法可以返回其返回型別的任何子類的物件。一個可能的應用場景:API 可以返回物件,而不需要將它們的類公之於眾,以這種方式隱藏實現類將使這個 API 變得十分緊湊。這個技術適合於基於介面的框架(interface-based frameworks)。

  • 隨著輸入引數的不同,同一個靜態工廠方法可以返回不同的子類。例如 EnumSet 類:

    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");
    
        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
    

    當底層列舉型別的元素不超過64個時,返回 RegularEnumSet,超過64個則返回 JumboEnumSet

  • 在編寫包含靜態方法的類時,返回物件的類不需要存在。這種靈活的靜態工廠方法構成了服務提供者框架(service provider frameworks)的基礎,比如Java的資料庫連線 API(JDBC)。

靜態工廠方法的缺點

  • 只提供靜態工廠方法、不提供 publicprotected 構造器的類不能被繼承(子類化)。這也可能因禍得福:它鼓勵程式設計師使用組合而不是繼承。
  • 靜態工廠方法很難被程式設計師找到。在API文件中,靜態工廠方法不能像構造器那樣突出,這使得程式設計師難以找到它們來例項化一個類。

一些靜態工廠方法的常用名稱

  • from:一個型別轉換方法,接受一個引數並返回這個型別的相應例項。

    Date d = Date.from(instant);
    
  • of:一個聚合方法,接受多個引數、將它們合併在一起並返回這個型別的相應例項。

    Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
    
  • valueOffromto 的更為詳細的替代方式。

    BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
    
  • instancegetInstance:返回一個例項,該例項由其引數(如果有的話)描述,但不能說具有相同的值。

    StackWalker luke = StackWalker.getInstance(options);
    
  • createnewInstance:與 instancegetInstance 類似,除了該方法保證每個呼叫返回一個新的例項。

    Object newArray = Array.newInstance(classObject, arrayLen);
    
  • getType:與 getInstance 類似,當工廠方法在不同的類中時使用。Type 是工廠方法返回物件的型別。

    FileStore fs = Files.getFileStore(path);
    
  • newType:與 newInstance 類似,當工廠方法在不同的類中時使用。Type 是工廠方法返回物件的型別。

    BufferedReader br = Files.newBufferedReader(path);
    
  • typegetTypenewType 的簡潔版。

    List<Complaint> litany = Collections.list(legacyLitany);