= バインディングをオーバライドする方法 = 以下の元ネタは、[[http://publicobject.com/2008/05/overriding-bindings-in-guice.html|Overriding Bindings in Guice]] == ProductionModule == 製品で使用するモジュールは以下であるとする。 {{{ public class ProductionModule extends AbstractModule { protected void configure() { bind(LoginService.class).to(RemoteLoginService.class); bind(DataStore.class).to(MySqlDatabase.class); bind(BillerConnection.class).to(SecureBillerConnection.class); bind(LoggingEngine.class).to(DistributedLoggingEngine.class); } } }}} テスト時には、上のモジュールの一部だけを差し替えてテストを行いたいとする。例えば、LoginServiceはテスト用のクラスFakeLoginServiceにバインドしたい。どうすればよいか?単純に以下のようにしてしまうとエラーになってしまう。 {{{ Injector injector = Guice.createInjector(new ProductionModule(), new AbstractModule() { protected void configure() { bind(LoginService.class).to(FakeLoginService.class); } } ); }}} 一つのLoginServiceに対して複数のバインドが存在するからである。 == モジュールを分割する方法 == バインディングごとにモジュールを分割する方法がベストであるとのこと。 {{{ public class ProductionLoginModule extends AbstractModule { protected void configure() { bind(LoginService.class).to(RemoteLoginService.class); } } }}} これらの複数のモジュールを取捨選択してGuice.createInjectorに指定すればよい。 == 継承を使う方法 == ProductionModuleではLoginServiceのバインドする部分を一つのメソッドに追い出しておく。それを継承したTestModuleでは、そのメソッドをオーバライドする。 {{{ public class ProductionModule extends AbstractModule { protected void configure() { bindLoginService(); bind(DataStore.class).to(MySqlDatabase.class); bind(BillerConnection.class).to(SecureBillerConnection.class); bind(LoggingEngine.class).to(DistributedLoggingEngine.class); } protected void bindLoginService() { bind(LoginService.class).to(RemoteLoginService.class); } } public class TestModule extends ProductionModule { @Override protected void bindLoginService() { bind(LoginService.class).to(FakeLoginService.class); } } }}} == Moduleをオーバライドする方法 == ※注意:おそらく[[http://publicobject.com/2008/05/overriding-bindings-in-guice.html|Overriding indings in Guice]]ではベータ版の仕様で説明されているものと思われ、Guice2.0正規版では動作しない。正しくは以下のよう記述になる。 テスト用のモジュールを以下のように作成し、 {{{ public class OverridesModule extends AbstractModule { @Override protected void configure() { bind(LoginService.class).to(FakeLoginService.class); } } }}} 元のModuleを以下のようにオーバライドして新しいModuleを作成し、Injectorを生成する。 {{{ Module newModule = Modules .override(new ProductionModule()) .with(new OverridesModule()); Injector injector = Guice.createInjector(newModule); }}} == テスト用に一部モジュールを入れ替える == テスト時には常に製品用モジュールのバインディングの一部を差し替えて利用するとした場合、 テストユニットの中でいちいちModules.override()を呼び出すの面倒である。以下のようにしてみたらどうか。 {{{ import com.google.inject.*; import com.google.inject.util.*; public class TestInjector { public static Injector create(Module...overrideModules) { Module[]productionModules = new Module[] { new ProductionModule(), .... }; return Guice.createInjector( Modules.override(productionModules).with(overrideModules)); } } }}} これをテストユニットでは以下のように呼び出す。 {{{ Injector injector = TestInjector.create(new AbstractModule() { @Override protected void configure() { bind(LoginService.class).to(FakeLoginService.class); } }); }}} さらにもしデフォルトでテスト用のオーバーライドモジュールがある場合は、以下のようにも書ける。 overridesは何段になっても構わないようである。 {{{ public class TestInjector { public static Injector create(Module...overrideModules) { Module[]productionModules = new Module[] { new ProductionModule(), .... }; Module[]testModules = new Module[] { new TestModule(), .... }; return Guice.createInjector( Modules.override( Modules.override(productionModules).with(testModules) ).with(overrideModules) ); } } }}} このようにしておき、さらに個々のテストユニットで好みのオーバーライドが可能だ。 {{{ Injector injector = TestInjector.create(new AbstractModule() { @Override protected void configure() { bind(LoginService.class).to(FakeLoginService.class); } }); }}} == おまけ~PriviteModuleの使い方 == PrivateModuleを使うと、 * exposeしたバインディングだけが外部から使用でき、 * それ以外のバインディングは内部からのみ使用できる。 以下では、C.classに対するバインディングを複数作成しているが、CImplOneはAからのみ使用可能で、CImplTwoはBからのみ使用可能になる。 {{{ package test; import com.google.inject.*; public class Main { public interface A { } public static class AImpl implements A { @Inject C c; public String toString() { return c.getClass().toString(); } } public interface B { } public static class BImpl implements B { @Inject C c; public String toString() { return c.getClass().toString(); } } public interface C { } public static class CImplOne implements C { } public static class CImplTwo implements C{ } public static void main(String[]args) { Injector injector = Guice.createInjector( new PrivateModule(){ protected void configure() { binder().bind(A.class).to(AImpl.class); binder().bind(C.class).to(CImplOne.class); expose(A.class); } }, new PrivateModule(){ protected void configure() { binder().bind(B.class).to(BImpl.class); binder().bind(C.class).to(CImplTwo.class); expose(B.class); } } ); A a = injector.getInstance(A.class); B b = injector.getInstance(B.class); System.out.println(a.toString()); System.out.println(b.toString()); } } }}} 実行結果は {{{ class test.Main$CImplOne class test.Main$CImplTwo }}} [[guice/RobotLegProblem|Robot Leg Problem]]も参照のこと。