1. 程式人生 > >Effective Java之靜態工廠代替構造器(一)

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了。