使用静态工厂方法而不是构造器
注意:靜態工廠方法不是設計模式中的工廠方法。
一個類向客戶端提供靜態工廠方法有如下好處:
- 有名稱,不用根據參數類型和順序區分重載方法,讓代碼更易讀
- 是否每次調用都需要新對象是可控制的,對于不可修改的對象可以采取緩存對象來提高性能,例如可以使用==來判斷對象是否相等,而不使用equals,可以提高性能。
- 工廠方法體內可返回返回類型的任何子類型,這在選擇返回對象的類型上有很大的靈活性。一個對外隱蔽了實現類(即所返回的對象的類型對外是不可見的)的API是非常緊湊的API。這種技巧本身把自己引導成了基于接口的框架(接口為靜態工廠方法提供返回類型)。接口不能有靜態方法,因此按慣例返回類型為接口Type的靜態工廠方法會放在一個不可實例化的類Types中:例如Java集合框架中的Collections有32個對集合接口的非常方便的實現,分別提供不可修改集合、同步集合等等,幾乎所有這些實現都是通過一個不可實例化的類(java.util.Collections)的靜態工廠方法導出,這些工廠所返回的類都是非public的。當前集合框架API比導出32個public類要更小,不單只在API的代碼量上得到減少,同時還在概念的重量級方面。用戶一眼就知道所返回的對象正如接口所描述的一樣,不需要讀取額外的類實現文檔。另外,使用這樣一種靜態工廠方法需要客戶端使用接口來引用這個工廠方法所返回的對象,而不是使用實現類引用,這是一個好的實踐。不只由靜態工廠返回的對象的類型可以是非public的,根據傳入靜態工廠的參數的不同,這個返回對象的類也是可變的,只要是靜態工廠方法聲明的返回值類型的子類型就都是允許的。靜態工廠返回值類型也是可以在各發布版間變化的,以增強可維護性和性能。Java1.5引入的java.util.EnumSet沒有public構造器,只有靜態工廠,這些靜態工廠根據底層枚舉類型的大小返回兩個實現中的一個:如果元素小于等于64,靜態工廠返回一個RegularEnumSet實例,它由單個long支持; 如果枚舉類型有大于64個元素,靜態工廠會返回一個JumboEnumSet實例,它由一個long數組支持。這兩個實現類對客戶端是不可見的。
???? 由靜態工廠方法返回的對象的類甚至不必在編寫靜態工廠方法所在的類時就存在。靜態工廠的這種靈活性是服務提供者框架(service provider framework)的基石,例如JDBC。服務提供者框架是一個系統,在這個系統中,服務提供者實現服務,這個系統讓這些實現對客戶端可用,從而把客戶端與實現分離。
???? 服務提供者框架有三個基本的組件:由提供者實現的服務接口、系統用于注冊服務實現并讓其對客戶端可用的提供者注冊API、客戶端用于獲取服務實現的服務訪問API。服務訪問API通常允許讓客戶端指定某些條件以便選擇一個提供者,但客戶端不是必須要指定,如果沒有指定,API會返回一個默認實現。服務訪問API就是構成服務提供者框架的基石的靈活靜態工廠。
??? 服務提供者接口有一個可選的組件:服務提供者接口,由提供者用于創建他們自己的服務實現的實例。在沒有服務提供者接口的情況下,實現是通過類名進行注冊,并通過反射進行實例化。在JDBC中,Connection就是服務接口,DriverManager.registerDriver()就是服務提供者注冊接口,DriverManager.getConnection()是服務訪問接口,Driver是服務提供者接口。
??? 現在有許多服務提供者框架模式的變種,例如,通過使用適配器,服務訪問API可以返回比提供者所需要的服務接口更加豐富的服務接口,下面是一個服務提供者接口及其一個默認實現:
// Service provider framework sketch
// Service interface
public interface Service {
??? ... // Service-specific methods go here
}
// Service provider interface
public interface Provider {
??? Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
??? private Services() { }? // Prevents instantiation (Item 4)
??? // Maps service names to services
??? private static final Map<String, Provider> providers =
??????? new ConcurrentHashMap<String, Provider>();
??? public static final String DEFAULT_PROVIDER_NAME = "<def>";
// Provider registration API
??? public static void registerDefaultProvider(Provider p) {
??????? registerProvider(DEFAULT_PROVIDER_NAME, p);
??? }
??? public static void registerProvider(String name, Provider p){
??????? providers.put(name, p);
??? }
// Service access API
??? public static Service newInstance() {
??????? return newInstance(DEFAULT_PROVIDER_NAME);
??? }
??? public static Service newInstance(String name) {
??????? Provider p = providers.get(name);
??????? if (p == null)
??????????? throw new IllegalArgumentException(
??????????????? "No provider registered with name: " + name);
??????? return p.newService();
??? }
}
- 減少創建參數化類型實現時的冗余信息
沒有使用靜態工廠時:
Map<String, List<String>> m = new HashMap<String, List<String>>();
使用靜態工廠后:
public static <K, V> HashMap<K, V> newInstance() {
??? return new HashMap<K, V>();
}
Map<String, List<String>> m = HashMap.newInstance();
遺憾的是,標準的集合類實現中,例如HashMap并沒有類似上面定義的工廠方法。但可以把這種方法放到自己的工具類中,更加重要的是,可以在自己的參數化類中提供這種靜態工廠方法。
?
只提供靜態工廠方法的類的主要缺點在于不能被子類化,因為沒有public或protected的構造器的類是不能被子類化的。
由public權限的靜態工廠返回的非public類也是不能被子類化的,例如,不能子類化Collections里面的實現類,這可以說是因禍得福,因此這樣可以促進程序員使用組合而不是繼承。
另一個缺點在于靜態工廠方法不易與其它靜態方法區分開。
主要是因為靜態工廠方法不像構造器那樣明顯地出現在API文檔中,因此很難知道如何使用類中提供的靜態工廠來代替構造方法實例化對象。可以通過在類或接口中寫注釋來說明,并且讓靜態工廠方法名遵循約定:
valueOf、of、getInstance、newInstance、getType、newType
總之,靜態工廠和構造方法各有優缺點,但一向在使用構造器之前優先考慮使用靜態工廠。
轉載于:https://www.cnblogs.com/mark-chan/p/5401666.html
總結
以上是生活随笔為你收集整理的使用静态工厂方法而不是构造器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好友消息和群消息区别
- 下一篇: 理解函数指针