正如〈Inversion of Control?〉中談到的,控制、熟悉應用程式的流程走向,是很重要的一部份,這對於評估是否使用某個框架,瞭解換取而來的益處是否超越了犧牲掉的流程自由度是有幫助的,這才能使得框架選用具有意義。
在 BeforeDI 這個範例專案中,為了要能連線 H2 資料庫,在 cc.openhome.Main 寫了底下的流程:
Properties prop = new Properties();
prop.load(Main.class.getClassLoader().getResourceAsStream("jdbc.properties"));
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(prop.getProperty("cc.openhome.jdbcUrl"));
dataSource.setUser(prop.getProperty("cc.openhome.user"));
dataSource.setPassword(prop.getProperty("cc.openhome.password"));
AccountDAO acctDAO = new AccountDAOJdbcImpl(dataSource);
MessageDAO messageDAO = new MessageDAOJdbcImpl(dataSource);
UserService userService = new UserService(acctDAO, messageDAO);
userService.messages("caterpillar")
.forEach(message -> {
System.out.printf("%s\t%s%n",
message.getLocalDateTime(),
message.getBlabla());
});
這個片段從類別路徑中的 jdbc.properties 中讀取設定,建立了 JdbcDataSource
,作為 AccountDAO
、MessageDAO
建構實作物件之用,接著再將兩個 DAO,作為 UserService
的依賴物件,然後才使用 UserService
來做些操作。
物件的建立與相依注入,當然是建立應用程式時必要的關切點,只不過當過程太過冗長,模糊了商務流程之時,應該適當地將之分離。
就這個片段來說,真正的目的其實是取得 UserService
來做些操作,若想隔離相依注入這個關切點,也許建立一個工廠方法會比較好:
package cc.openhome;
import java.io.IOException;
import java.util.Properties;
import org.h2.jdbcx.JdbcDataSource;
import cc.openhome.model.AccountDAO;
import cc.openhome.model.AccountDAOJdbcImpl;
import cc.openhome.model.MessageDAO;
import cc.openhome.model.MessageDAOJdbcImpl;
import cc.openhome.model.UserService;
public class Service {
public static UserService getUserService() throws IOException {
Properties prop = new Properties();
prop.load(Main.class.getClassLoader().getResourceAsStream("jdbc.properties"));
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL(prop.getProperty("cc.openhome.jdbcUrl"));
dataSource.setUser(prop.getProperty("cc.openhome.user"));
dataSource.setPassword(prop.getProperty("cc.openhome.password"));
AccountDAO acctDAO = new AccountDAOJdbcImpl(dataSource);
MessageDAO messageDAO = new MessageDAOJdbcImpl(dataSource);
UserService userService = new UserService(acctDAO, messageDAO);
return userService;
}
}
這麼一來,商務流程的部份,就可以藉由這個工廠方法來取得 UserService
,不用在乎如何建立、相依注入等細節:
package cc.openhome;
import java.io.IOException;
import cc.openhome.model.UserService;
public class Main {
public static void main(String[] args) throws IOException {
UserService userService = Service.getUserService();
userService.messages("caterpillar")
.forEach(message -> {
System.out.printf("%s\t%s%n",
message.getLocalDateTime(),
message.getBlabla());
});
}
}
如此之來,程式碼的流程清晰了,而且即使是不懂 JDBC 或 DataSource
等的開發者,只要透過這樣的方式,也可以直接取得 UserService
進行操作。
上面的 Service
當然是特定用途,隨著打算開始整合各種程式庫或方案,你會遇到各種物件建立與相依設定需求,為此,你可能會重構 Service
,使之越來越通用,像是可透過組態檔來進行相依設定,甚至成為一個通用於各式物件建立與相依設定的容器,實際上這類容器,在 Java 的世界中早已存在,且有多樣性的選擇,而最有名的實現之一就是 Spring 框架。