Java 使用靜態(tài)工廠方法代替構(gòu)造器

最近看到一段代碼,感覺寫得非常好,它的功能是帶參數(shù)啟動 Activity:

public class SecondActivity extends Activity {

    public static void actionStart(Context context, String data1, String data2) {
        Intent intent = new Intent(context, SecondActivity.class);
        intent.putExtra("param1", data1);
        intent.putExtra("param2", data2);
        context.startActivity(intent);
    }
    ...
}

使用靜態(tài)方法的好處是什么呢?首先是一目了然,SecondActivity 需要的數(shù)據(jù)在方法參數(shù)中全部體現(xiàn)出來了,這樣即使不閱讀 SecondActivity 的代碼,不用詢問負(fù)責(zé)編寫 SecondActivity 的同事,也可以清楚地知道啟動 SecondActivity 需要傳遞哪些數(shù)據(jù)。另外,這樣寫還簡化了啟動 Activity 的代碼,調(diào)用者只需要使用一行代碼就可以啟動 Activity,真的是非常簡便!

SecondActivity.actionStart(FirstActivity.this, "data1", "data2");

這讓我想到了《Effective Java 第2版》書中講的一條經(jīng)驗(yàn):考慮使用靜態(tài)工廠方法代替構(gòu)造器。這里的靜態(tài)工廠方法與設(shè)計模式中的工廠方法模式不同,它是用于創(chuàng)建當(dāng)前類對象的靜態(tài)方法。

提供靜態(tài)工廠方法而不是公有的構(gòu)造器的好處是什么呢?

  • 靜態(tài)工廠方法有名稱,可以清晰地閱讀。如果構(gòu)造器的參數(shù)本身沒有確切地描述返回的對象,那么具有適當(dāng)名稱的靜態(tài)工廠方法會更容易使用。

    舉個栗子:Executors 類里面有一系列構(gòu)建線程池的靜態(tài)工廠方法,根據(jù)方法名就能知道返回的線程池的類型,清晰簡潔,對使用者來說非常友好。

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    public static ExecutorService newFixedThreadPool(int nThreads) {
          return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
  • 不必在每次調(diào)用靜態(tài)工廠方法的時候都創(chuàng)建一個新對象。這使得不可變類可以使用預(yù)先構(gòu)建好的實(shí)例,或者將實(shí)例緩存起來進(jìn)行復(fù)用,從而避免創(chuàng)建不必要的重復(fù)對象。

    舉個栗子:Boolean 類的 valueOf 方法,返回預(yù)先構(gòu)建的緩存的實(shí)例,實(shí)例的復(fù)用提升了軟件的性能。

    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
    
    public static Boolean valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE;
    }
    
  • 靜態(tài)工廠方法可以返回原返回類型的任何子類型的對象,這樣我們在選擇返回對象的類時就有了更好的靈活性。

    舉個栗子:EnumSet 類的 noneOf 方法返回一個空的枚舉集合,類型是 EnumSet 的子類,這樣根據(jù)不同的條件就能得到不同的實(shí)現(xiàn)類的實(shí)例,提高了方法的靈活性。

    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        if (!elementType.isEnum()) {
            throw new ClassCastException(elementType.getClass().getName() + " is not an Enum");
        }
        E[] enums = Enum.getSharedConstants(elementType);
        if (enums.length <= 64) {
            return new MiniEnumSet<E>(elementType, enums);
        }
        return new HugeEnumSet<E>(elementType, enums);
    }
    
  • 使用靜態(tài)工廠方法創(chuàng)建參數(shù)化類型的實(shí)例,代碼會變得非常簡潔

    舉個栗子:使用靜態(tài)工廠方法 newInstance 創(chuàng)建帶參數(shù)的 Fragment,參數(shù)清晰、代碼簡潔,使用起來非常容易。

    public class BlankFragment extends Fragment {
    
            public static BlankFragment newInstance(String param1, String param2) {
                  BlankFragment fragment = new BlankFragment();
                  Bundle args = new Bundle();
                  args.putString("param1", param1);
                  args.putString("param2", param2);
                  fragment.setArguments(args);
                  return fragment;
            }
            ...
    }
    

靜態(tài)工廠方法的命名也有標(biāo)準(zhǔn),下面是一些慣用名稱:

  • valueOf: 這是類型轉(zhuǎn)換的方法,返回的實(shí)例與它的參數(shù)具有相同的值。
  • of:valueOf 的一種更為簡潔的替代,在 EnumSet 中使用并流行起來。
  • getInstance:返回的實(shí)例是通過方法的參數(shù)來描述的,對于單例來說,返回唯一的實(shí)例。
  • newInstance:像 getInstance 一樣,但是可以確保返回的每個實(shí)例都與其他的實(shí)例不同。
  • getType:像 getInstance 一樣,但是在工廠方法處于不用的類中的時候使用。
  • newType:像 newInstance 一樣,但是在工廠方法處于不用的類中的時候使用。

總而言之,靜態(tài)工廠方法的優(yōu)勢還是很明顯的,簡潔易讀,方便使用。所以在編碼的過程中,還是要考慮使用靜態(tài)工廠方法,不要一味地使用構(gòu)造器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容