Inside a module, if I need to provide a different implementation of an interface based on a variable known at module construction time I can put the logic inside the @Provides method for that interface type. Like so:
@Module
public class FooModule {
private final State state;
public FooModule(State state) {
this.state = state;
}
@Provides
FooInterface provideFooImplementation() {
switch(state) {
case STATE_1:
return new FooImpl1();
case STATE_2:
return new FooImpl2();
...
case STATE_10:
return new FooImpl10();
}
}
}
However, these implementations could be created by Dagger. I would rather say "Hey, based on X I want you to instantiate this class for me"
I have considered a couple of options.
Change the provides method to take in all of the possible implementations:
@Provides FooInterface provideFooImplementation(FooImpl1 impl1, FooImpl2 imp2, ..., FooImpl10 impl10) { switch(state) { case STATE_1: return impl1; case STATE_2: return impl2; ... case STATE_10: return impl10; } }
This allows Dagger to instantiate them and satisfy all of their dependencies but it's not a good idea if each of the implementations is relatively large or expensive to create.
Change the provides method to take in a collection of all of the dependencies for the different implementations.
@Provides FooInterface provideFooImplementation(Context context, Repo repo, HttpClient httpClient, ...) { switch(state) { case STATE_1: return new FooImpl1(context); case STATE_2: return new FooImpl2(repo, httpClient); ... case STATE_10: return new FooImpl10(context, repo); } }
This is slightly better than option 1 in that Dagger doesn't have to instantiate every single implementation, however it still needs to instantiate all of the dependencies even though they might not be used in all cases. I'm also back to creating the objects myself even though they could be created by Dagger.
Create one module per implementation and instantiate the appropriate module. So something like:
@Module public FooImpl1Module { @Provides FooInterface provideFooImplementation(Context context) { return new FooImpl1(context); } }
This would be fine, but now I have issues defining the component that depends on the module.
What's the best way to tackle this problem?
One suggestion has been to try option 1 with the parameters wrapped in Lazy. Then I only end up calling .get() on one. I'll try this out when I can and post the results
Have you tried something like this?
Rather than
Lazy<T>
, do option 1 withProvider<T>
.Lazy<T>
is just aProvider<T>
that memoizes locally (with the necessary double-checked locking), but because you know you'll only call one Provider exactly once, you can just inject the Provider instead and skip the synchronization overhead.Option 2 will work, but you'll effectively skip the dependency wiring that Dagger could easily do for you, and Option 3 won't work as stated because your @Component annotation needs your list of modules to be a compile-time constant for your Dagger code generation to work.
(A variant of Option 3 could work if your binding were to a constant or a zero-dependency class of one form or another, because then you could pass in an arbitrary subclass of your Module into your Component builder. However, Dagger can only analyze the bindings in the superclass module, and you'd have trouble if your @Provides method implementations take different parameters as yours would, so the
switch
is the best and clearest alternative I can think of.)A possible solution would be using
@Named("foo")
annotation in conjunction with favoring component provision method over manual injection, which would however mean that your state would be independent from the module itself, and you'd be the one to make your choiceThen you can call