2020/06/22

[筆記] Effective Java #1 用 static factory methods 代替 constructor

Effective Java 3rd 簡體中文版筆記 #1 用 static factory methods 代替 constructor
什麼是 static factory method? 簡單來說,就是在 class 中回傳自身的 static method,像是在 IntegerBooleanvalueOf
public static Long valueOf(String s) throws NumberFormatException { }
public static String valueOf(Object obj) { }
static factory method 相較於 constructor 的優勢
1. static factory method 是方法,所以具有名稱,對開發者來說較簡單明確,例如:電信公司手機的計費方式 PricingModel 有很多種,利用方法名稱讓開發者在使用更容易。
public static PricingModel newComer();
public static PricingModel general();
public static PricingModel enterprise();
2. 對不可變物件 (immutable object) 來說,不需要每次都產生新的實體 (instance),像是 java.lang.BooleanvalueOf,在傳入相同的 String s 時皆會回傳同一實體。當生成新實體的成本很高時,這樣能提升效能,預先建好實體,讓相同的調用拿到同樣的實體。
package java.lang;

public final class Boolean implements java.io.Serializable, 
                                          Comparable<Boolean> {

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

    public static Boolean valueOf(String s) {
        return parseBoolean(s) ? TRUE : FALSE;
    }
}
下面的範例搭配 private constructor,讓外部無法自由的生成 PricingModel,若要使用 PricingModel 就必須調用 static factory method。
public class PricingModel {
    private static final PricingModel GENERAL_MODEL = ...;
    public static PricingModel general() {
        return GENERAL_MODEL;
    }
    
    private PricingModel() { }
}
3. static factory method 可以回傳任何繼承它的 subclass。例如:電信公司提供給員工的優待方案,定義為 StaffPricingModel 且該 class 不是 public class。
class StaffPricingModel extends PricingModel {
    private static final StaffPricingModel INSTANCE = ...;
    static PricingModel employee() {
        return StaffPricingModel.INSTANCE;
    }
}
類似的用法可在 java.util.Collections 裡見到,其中 emptyList 是回傳一不可變實體,而該實體的 class 是 EmptyListEmptyList 定義在 Collections 中,是繼承 AbstractList 的一個 static private class。
public class Collections {
    public static final List EMPTY_LIST = new EmptyList<>();
    public static final <T> List<T> emptyList() {
        return (List<T>) EMPTY_LIST;
    }
    
    private static class EmptyList<E> extends AbstractList<E> 
        implements RandomAccess, Serializable {
    }
}
4. 可以根據開發者傳入的參數,來決定要回傳的實體。像是企業計費方案,可以依照是否是公務機來決定回傳的 PricingModel
public static PricingModel enterprise(boolean business) {
    return business ? BUSI_MODEL : ENTERPRISE_MODEL;
}
5. static factory method 回傳的型別,在編寫時可以不存在,像是 Service Provider Framework,類似的應用有 JDBC API。
Service Provider Interface
https://stackoverflow.com/questions/2954372/difference-between-spi-and-api
static factory method 會遇到的問題
1. 如果該 class 沒有提供 public 或 protected constructor,就無法被繼承。因為大部分使用 static factory method 時,會伴隨著 private constructor,如果沒有再多定義其他的 constructor 就無法被繼承,但同時也保證開發者只能調用該 class 的 static factory method 來拿到實體。
public class PricingModel {
    private PricingModel() { ... }
    private static final PricingModel INSTANCE = new PricingModel();
}

// error: There is no default constructor available in 'PricingModel'
public class FreePricingModel extends PricingModel { }
2. 開發者很難發現 static factory method 的存在,在 Java doc 中,沒有像 constructor 一樣,明確的標示出來。static factory method 常見的命名規則如下,在設計上應優先考慮 static factory method 而非直接提供 public constructor。
  • from:型態的轉換,像是 Date.from(Instant instant)
  • ofvalueOf:像是 Boolean.valueOf(String s)
  • getInstancenewInstance:後者需保證每次回傳的都是新實體。
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2020/06/effective-java-1-static-factory-methods.html

沒有留言:

張貼留言