In my project, I use dependency injection everywhere, and I use ad hoc factories for two cases. First, when I want to control exactly when an instance is created, I inject a factory instead of an instance:
// WidgetA must be created before WidgetB, because of the side-effects
// on the container.
WidgetAFactory.make(container);
WidgetBFactory.make(container);
The other case is when the constructor takes a mix of injectable values and runtime values. Instead of using:
@Inject
WidgetC(
Container,
@WidgetCFont Font,
@WidgetCColor Color,
@Named("flag") String flag) {
...
}
I use:
@Inject
WidgetCFactory(
@WidgetCFont Font font,
@WidgetCColor Color color,
@Named("flag") String flag) {
...
}
WidgetCFactory.make(Container container) {
return new WidgetC(container, font, color, flag);
}
But I am being hit by two limitations with my use of factories:
In my first exanple, I also need WidgetA to be a @Singleton that will be needed by other @Injected constructors. So far, my solution is to store the instance I created when calling the factory, and @Provides it for others to use. Is there a way to give the control of this singleton back to guice without having to maintain that instance myself?
In my second example, managing the injected dependencies is a mess: the WidgetCFactory has to call the WidgetC constructor with a long list of injected values, that has to be updated for each change in the dependencies, with no annotation checks. Is there a way to provide Guice with the runtime parameter, and let it deal with the other dependencies?
It feels like for both cases, I could use a child injector that would be given the runtime value, and let Guice be the factory:
public static class WidgetCFactory {
private final Injector injector;
@Inject
public WidgetCFactory(Injector injector) {
this.injector = injector;
}
public WidgetC make(Container container) {
Injector childInjector = injector.createChildInjector(new AbstractModule() {
@Override protected void configure() {
bind(Container.class).toInstance(container);
}
});
return childInjector.getInstance(WidgetC.class);
}
}
But I don't find a lot of cases of people doing this. Is it because it's too heavy, or outside the good practices of dependency injection? What would be a better way?
Mixing injected and runtime values means you should look into "assisted injection", which lets you declare certain injected values to be provided at runtime by the callsite, and for a factory to be generated which will expose only those as a parameter. From https://github.com/google/guice/wiki/AssistedInject, you'll want to install a module for each type to be handled in this way, something like
// this goes in your existing Module.configure()
install(new FactoryModuleBuilder()
// you can add more than one type here in this way
.implement(WidgetC.class, WidgetC.class)
.build(WidgetFactory.class));
//...
public interface WidgetFactory {
// you can add more than one method here
WidgetC createWidgetC(Container container);
}
@AssistedInject
WidgetC(
@Assisted Container,
@WidgetCFont Font,
@WidgetCColor Color,
@Named("flag") String flag) {
...
}
Note especially the change to the WidgetC
constructor, both the different annotation on the constructor (since it is not in fact safe to construct via normal injection) and on the Container
parameter (which will be provided by the factory, not the IoC container.
To make WidgetA
a singleton, you can either decorate the type with @Singleton
, or bind it in your configure()
method:
bind(WidgetA.class).in(Singleton.class);
As written, it will be lazily created - it will only exist after it is first requested, but then every time it is requested, it will be the same instance and will not be created from scratch.