Search code examples
gwtguicegwt-gin

Guice/Gin. How to inject multiple implementations


I have a webapp that use GIN to inject dependencies at entry point.

private InjectorService injector = GWT.create(InjectorService.class);

@GinModules({PlaceContollerInject.class, RootViewInject.class})
public interface InjectorService extends Ginjector {

  RootView getRootView();
  PlaceController getPlaceConroller();

}

public class RootViewInject extends AbstractGinModule {

  @Override
  protected void configure() {
    bind(RootView.class).to(RootViewImpl.class);
  }
}

I need a mobile version that use different RootView implementation. Dependencies are described in the following module

public class RootViewMobileInject extends AbstractGinModule {

  @Override
  protected void configure() {
    bind(RootView.class).to(RootViewMobileImpl.class);
  }
}

The question is how to choose needed dependency conditionally whether we need mobile or default version. I've seen GWT-GIN Multiple Implementations, but haven't figured out that solution because the Provider breaks the dependencies' chain and the Factory Pattern breaks testability. In "Big Modular Java with Guice" video here (12 minute) Guice's injector with modules was presented as a replacement to Factories. So my question is should I create different Ginjector for mobile and default versions (like MobileFactory and DefaultFactory) of my app or it would be bad practice and I should configure one instance of Ginjector with all needed versions. For example with the annotation bindings like this.

public class RootViewMobileInject extends AbstractGinModule {

  @Override
  protected void configure() {
    bind(RootView.class).annotatedWith(Mobile.class).to(RootViewMobileImpl.class);
  }
}

and use @Mobile annotated bindings at GWT entry point

  @Inject
  private void setMobileRootView(@Mobile RootView rw) {
    this.rw = rw;
  }

In such a simplified example as above it might be possible. But if an application have more dependencies which need mobile and default versions. It looks like back to untestable "ugly" (as at Guice's presentation was said) factories. Sorry for my English. Any help is appreciated.


Solution

  • I believe you'll want to use GWT deferred binding, using class replacement to bind a different version of your InjectorService depending on the user-agent. This will ensure the mobile version only has the mobile implementations compiled in (and downloaded)

    So you would have InjectorServiceDesktop, InjectorServiceMobile, which both extend from InjectorService, then GWT.create(InjectorService.class), and let deferred binding decide which implementation it should use.

    http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsDeferred.html#replacement

    One instance of Ginjector with all versions seems bad as it means all code for both versions is always downloaded (and you certainly don't want to download all your desktop views into your mobile app)

    EDIT: As Thomas points out in the comments, since the Injectors are generated classes, you'll need to put each InjectorServiceXXX inside a simple holder class that GWT.create()'s the InjectorServiceXXX, and use replacement to switch between the holders.