Effective Java之靜態工廠代替構造器(一)
優勢1:它們有名稱,所以在多個構造器時,能夠根據靜態工廠的方法的名稱找到哪個構造器。
優勢2:能夠實現單例模式,不必在每次呼叫重新建立新物件。
優勢3:當建立引數化引數例項時,使用靜態工廠方法更加簡單。
優勢4:它們可以返回原返回型別的任何子物件。
書中提到了展示這個優勢的“服務提供者框架”,我們以jdbc為例,來看一下jdbc是如何利用靜態工廠的。
定義
多個服務提供者實現一個服務,系統為服務提供者的客戶端提供多個實現,並把他們從多個實現中解耦出來。——–《Effective Java》
元件:
服務介面(Service Interface):提供者需要去實現的。 提供者者註冊API(provider Registration API):系統用來註冊實現的。 服務訪問API(Service Access API):客戶端用來獲取例項的。 服務提供者API(Service Provider Interface)(可選):建立其服務例項物件的。
使用方式:
Class.forname("com.mysql.jdbc.Driver");
connection=DriverManager.getConnection("jdbc:mysql://localhost/Contacts?serverTimezone=UTC", "root", "Cc229654512");
看上去,短短兩個api就能通過mysql服務商得到connection服務介面,我們看看具體是如何實現的:
實現
mysql的driver原始碼:
package com.mysql.jdbc;
import com.mysql.jdbc.NonRegisteringDriver;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can\'t register driver!");
}
}
}
DriverManager原始碼
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
java.sql.DriverManager.java( 這是getConnection方法中的片段)
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}
過程:
Class.forName(“”)方法的作用是將這個class載入到虛擬機器中,此時這個class執行了它的static程式碼塊(而不是建構函式),從它的原始碼中,我們可以看到,它呼叫了自己的構造器,並將其註冊到DriverManager中;一個反射方法實際上幹了很多事情。
connection=DriverManager.getConnection()方法從原始碼看到,實際上就是遍歷註冊到它身上的driver,根據driver得到connection。
總結:
在jdbc中,
connection就是服務介面,也就是,得到它就得到了各種服務;
driver 服務提供者介面,也就是,得到它,它就能提供服務介面,這裡就是connection。
DriverManager.getConnection()是服務訪問API,也就是通過這個方法,能得到connection這個服務介面。
DriverManager.registerDriver()是提供者註冊API
mysql或者是oracle服務商提供了connection的不同實現,通過自己的服務提供者介面(mysql的driver)註冊到DriverManager中,我們可以看到DriverManager 並不是mysql或者是oracle服務商提供的類,而是java.sql提供的一個靜態工廠,我們通過DriverManager.getConnection()方法得到服務商提供的Connection實現。
簡單的,我們用一個對話理解:
sun公司:我把driverManager靜態工廠完成了,服務商,你只要實現自己的Connection,實現自己的driver,然後用DriverManager.registerDriver()把你的driver註冊進來就可以了,我可以利用靜態工廠方法DriverManager.getConnection()返回你的Connection實現。
mysql服務商:好的,我註冊好了,使用者,你只需要呼叫
Class.forname(“com.mysql.jdbc.Driver”);我就幫你把我的driver註冊進去,你可以直接通過DriverManager.getConnection()就能得到我實現的Conncetion了。