9
Aug 2014
by
佐々木 亜里沙
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"); } }