通常のインジェクション
Google Guiceを使ったインジェクションはこんな感じになります。
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(Logic.class).to(LogicImpl.class);
}
});
Logic logic = injector.getInstance(Logic.class);
logic.doLogic();
...
問題点
ここのLogicImpl.classをソースに直で書いているので違う実装を使おうと思ったらソースを変更するしかありません。JUnit等でテストする場合はそれで問題ないのですが、実際の業務で使おうと思った場合はこれだと柔軟性に欠けます。
対応
ここはClass<?>を指定できればいいので外部ファイルに出してしまって Class.forName()でClass<?>を得れば柔軟性が上がります。
ConfigBean config = JAXB.unmarshal(file, ConfigBean.class);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
@SuppressWarnings("unchecked")
Class<? extends Logic> cls = (Class<? extends Logic>)Class.forName(config.getLogic());
bind(Logic.class).to(cls);
}
});
こうすることで、
- ファイル受信処理の骨格は共通
- Aシステムから来たファイルは
LogicAImplで処理 - Bシステムから来たファイルは
LogicBImplで処理
ということを実行時に解決させることができます。
発展
さらに、LogicAImplやLogicBImplの中で使っているインタフェースも同じようにインスタンス化したい場合、だけどLogicAImplとLogicBImplで使っているものが違う場合
例えば
-
LogicAImplではMasterADAO -
LogicBImplではMasterBDAO
を使っているとすると、上記の方法だとちょっとうまくありません。不要なクラスまでインスタンス化してしまいます。
こういうときは
// インタフェース名を定義ファイルから取得
@SuppressWarnings("rawtypes")
Class intf = Class.forName(config.getInterface());
// 実装クラス名を定義ファイルから取得
@SuppressWarnings("rawtypes")
Class impl = Class.forName(config.getImplement());
bind(intf).to(impl);
こうしておきます。
Class<?>にはできないので、あえてClassを使っています。
定義ファイルにインタフェース名と実装クラス名を書いておけば、Logicをインスタンス化した場合に引きずられてインスタンス化してくれます。
定義ファイルはこんな感じで用意すればいいのではないでしょうか。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<file type="A">
<logic implements="xxx.yyy.LogicAImpl">
<with interface="xxx.yyy.dao.MasterADAO" implement="xxx.yyy.dao.impl.MasterADAOImpl" />
<with interface="xxx.yyy.Parser" implement="xxx.yyy.util.CSVParser" />
</logic>
</file>
<file type="B">
<logic implements="xxx.yyy.LogicBImpl">
<with interface="xxx.yyy.dao.MasterBDAO" implement="xxx.yyy.dao.impl.MasterBDAOImpl" />
</logic>
</file>
</configuration>