01、靜態工廠方法替代構造器

考慮用靜態工廠方法替代構造器


 考慮使用靜態工廠方法來替代構造器的原因:

  1. 靜態工廠方法有名稱:普通的構造器中,參數並不能很好地描述返回對象的特點,代碼的閱讀性不好。  

考慮下面的程序:

Random random = new Random();
BigInteger integer = BigInteger.probablePrime(3, random);

 該代碼的含義是返回一個很有可能是質數並且長度爲3的BigInteger對像,這裏的長度只的是將整數裝換成二進制後的長度;很有可能指的是:不是質數的概率不超過2-100

但是如果使用的是構造函數:BigInteger(int, Random),則很難通過參數來猜測返回的是什麼東西。


 2. 使用靜態工廠方法不需要每次調用的時候都創建一個新的對象。

如果一些不可變類在使用之前就已經創建好了實例,或者將構建好的實例緩存起來的話,那麼在實際使用的時候就能過直接拿過來用,避免了創建不必要的重複對象,下面還是通過已經實際應用的例子來看一下:

Boolean b = Boolean.valueOf(true);  

 這行代碼的含義就是創建一個值爲true的Boolean對象,我們看一下它的源碼:

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

  而TRUE和FALSE的定義如下:

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

  很明顯,這兩個被直接定義成了不可變類,可以重複使用。如果某些服務裏面需要經常創建相同的對象,並且創建對象的代價比較高,則使用這種方法可以顯著提高性能。


3. 使用靜態工廠方法可以返回原返回類型的任何子類型的對象。

如果API可以返回對象,但同時又不會是對象的類變成公有的,這種方法隱藏了實現類,同時也使API變得簡潔。

要說具體實例的話,Java Collections Framework的集合接口中,幾乎所有的實現都是通過一個靜態工廠方法在一個不可實例化的類中導出。

網絡盜圖:

 服務提供者框架(Service Provider Framework)

作者提到:靜態工廠方法返回的對象所屬的類,在編寫該靜態工廠方法的類時可以不必存在。就是多個服務提供者實現一個服務,系統爲服務提供者的客戶端提供多個實現,並把他們從多個實現中解耦出來。

服務提供者框架組件:服務接口(Service Interface)、提供者註冊API(Provider Registration API)、服務訪問API(Service Access API)以及可選組件 服務提供者接口(Service Provider Interface)。

感覺略難懂啊。。。biubiu~

對於JDBC:Connection是服務接口;DriverManager.RegisterDriver就是提供者註冊API;DriverManager.getConnection就是服務訪問API;Driver就是服務提供者接口。

(PS:是不是吧JDBC的東西忘了?還能手寫出來不?趕緊拿出小本本記下來。。。)

public static void main(String[] args) throws ClassNotFoundException, SQLException {
String URL = "jdbc:mysql://127.0.0.1:3306/study?useUnicode=true&characterEncoding=utf-8";
String USER = "root";//用戶名
String PASSWORD = "xxxxx";//你的MySQL密碼
//1.加載驅動程序
Class.forName("com.mysql.jdbc.Driver");
//2.獲得數據庫鏈接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.通過數據庫的連接操作數據庫,實現增刪改查(使用Statement類)
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from user");
//4.處理數據庫的返回結果(使用ResultSet類)
while (rs.next()) {
System.out.println(rs.getString("username") + " " + rs.getInt("age"));
}
//關閉資源
rs.close();
st.close();
conn.close();
}

  稍微有點不太規範哈,不過功能是可以實現的。其中,如果使用了Maven來管理項目的話,需要添加:

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.44</version>
    </dependency>

  下面還有一個具體的模板可供參考,以後可以直接拿過去稍微修改一下就可以用了,完美~:

//這是服務接口 Service Interface
public interface Service {
//各種方法
}

//這是服務提供者接口 Service Provider Interface
public interface Provider {
Service newService();
}

//這是一個不能直接實例化的類用來進行註冊和訪問的
public class Services {
private Services() { //禁止實例化
}

private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
public static final String DEAFULT_PROVIDER_NAME = "default";

//提供着註冊API Provider Registration API
public static void registerDefaultProvider(Provider provider) {
registerProvider(DEAFULT_PROVIDER_NAME, provider);
}
public static void registerProvider(String name, Provider provider) {
providers.put(name, provider);
}

//服務訪問API Service Access API
public static Service newInstance() {
return newInstance(DEAFULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider provider = providers.get(name);
if (null == provider) {
throw new IllegalArgumentException("No Provider registered with name" + name);
}
return provider.newService();
}
}

 缺點

任何方法或多或少都會優缺點,沒有最好的方法,只有最合適的方法,對一個新的方法要全面瞭解其優缺點,然後根據自己的實際情況,選擇一個最合適的方法。

1、類如果不含公有的或者受保護的構造器的話,就不能夠被子類化,沒毛病啊~

2、其實靜態工廠方法跟其他靜態方法實際上沒啥區別,畢竟實現方法是類似的嘛~


 

靜態工廠方法常用的名稱,含義也比較直白:

  • valueOf:一般來說返回的實例跟參數有相同的值。
  • of:比valueOf更簡潔,含義類似。
  • getInstance:返回一個具體的實例,可以指定參數進行具體化區別。如果是單例(Singleton)的話,無參數,都返回唯一的實例。
  • getType: 字面意思,返回對象類型,so easy。

 

參考:Effective Java 中文版(第二版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章