Android 使用靜態工廠方法代替構造器的實現
最近看到一段程式碼,感覺寫得非常好,它的功能是帶引數啟動 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); } ...
}
使用靜態方法的好處是什麼呢?首先是一目瞭然,SecondActivity 需要的資料在方法引數中全部體現出來了,這樣即使不閱讀 SecondActivity 的程式碼,不用詢問負責編寫 SecondActivity 的同事,也可以清楚地知道啟動 SecondActivity 需要傳遞哪些資料。另外,這樣寫還簡化了啟動 Activity 的程式碼,呼叫者只需要使用一行程式碼就可以啟動 Activity,真的是非常簡便!
SecondActivity.actionStart(FirstActivity.this, "data1", "data2");
這讓我想到了《Effective Java 第2版》書中講的一條經驗:考慮使用靜態工廠方法代替構造器。這裡的靜態工廠方法與設計模式中的工廠方法模式不同,它是用於建立當前類物件的靜態方法。
提供靜態工廠方法而不是公有的構造器的好處是什麼呢?
-
靜態工廠方法有名稱,可以清晰地閱讀。如果構造器的引數本身沒有確切地描述返回的物件,那麼具有適當名稱的靜態工廠方法會更容易使用。
舉個栗子:Executors 類裡面有一系列構建執行緒池的靜態工廠方法,根據方法名就能知道返回的執行緒池的型別,清晰簡潔,對使用者來說非常友好。
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>()); }
-
不必在每次呼叫靜態工廠方法的時候都建立一個新物件。這使得不可變類可以使用預先構建好的例項,或者將例項快取起來進行復用,從而避免建立不必要的重複物件。
舉個栗子:Boolean 類的 valueOf 方法,返回預先構建的快取的例項,例項的複用提升了軟體的效能。
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; }
-
靜態工廠方法可以返回原返回型別的任何子型別的物件,這樣我們在選擇返回物件的類時就有了更好的靈活性。
舉個栗子:EnumSet 類的 noneOf 方法返回一個空的列舉集合,型別是 EnumSet 的子類,這樣根據不同的條件就能得到不同的實現類的例項,提高了方法的靈活性。
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); }
-
使用靜態工廠方法建立引數化型別的例項,程式碼會變得非常簡潔
舉個栗子:使用靜態工廠方法 newInstance 建立帶引數的 Fragment,引數清晰、程式碼簡潔,使用起來非常容易。
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; } ... }
靜態工廠方法的命名也有標準,下面是一些慣用名稱:
- valueOf: 這是型別轉換的方法,返回的例項與它的引數具有相同的值。
- of:valueOf 的一種更為簡潔的替代,在 EnumSet 中使用並流行起來。
- getInstance:返回的例項是通過方法的引數來描述的,對於單例來說,返回唯一的例項。
- newInstance:像 getInstance 一樣,但是可以確保返回的每個例項都與其他的例項不同。
- getType:像 getInstance 一樣,但是在工廠方法處於不用的類中的時候使用。
- newType:像 newInstance 一樣,但是在工廠方法處於不用的類中的時候使用。
總而言之,靜態工廠方法的優勢還是很明顯的,簡潔易讀,方便使用。所以在編碼的過程中,還是要考慮使用靜態工廠方法,不要一味地使用構造器。
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。