Tagbangers Blog

Effective Java読み直し1-staticファクトリメソッド(2)

staticファクトリメソッドの3つ目の長所から

メソッドの戻り値型の、任意のサブタイプのオブジェクトを返すことができる

参考:サブタイプ=継承関係にあるクラス

メリット

  • 返すオブジェクトのクラスをpublicにすることなく、APIがオブジェクトを返せる(実装クラスを隠蔽できる)
  • staticファクトリメソッドに対する引数の値に応じ、呼び出しごとに返すオブジェクトを変えることができる

JDBCのgetConnectionメソッド

JDBCはServlet APIのように中身がないinterface群で、MySQLやOracleなどの各プロバイダがデータベースに接続するための実装を提供しています
クライアントはプロバイダ(DriverのClassクラス)を指定し、getConnectionメソッドをコールすることで、そのプロバイダが提供しているデータベースに接続するConnectionクラスを取得することができます。
これは、DriverMaqnager.getConnection()メソッドをstaticファクトリAPI(=任意のサブタイプのオブジェクトを返せる)とすることで実現しています。
クライアントはプロバイダというものを用いてどのConnectionを取得するか、 実行時に動的に決めることができます。

public class JDBCConnectionSample {
	public static void main(String[] args) {
		Connection con = null;
		try {
			con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
			System.out.println(con.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

DriverManagerクラス(抜粋)

public class DriverManager {
    // Prevent the DriverManager class from being instantiated.
    private DriverManager(){}

    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

    @CallerSensitive
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();
        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }
        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized (DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }
        println("DriverManager.getConnection(\"" + url + "\")");
        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;
        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;
                    }
                }
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }
        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }
        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }
}