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");
}
}