通常のインジェクション
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>