The dagger generates code at compile time, so you will not have the flexibility of a module like in Guice; instead of Guice being able to reflectively detect @Provides methods and run the configure() reflexive method, Dagger will need to know how to create every implementation that may be needed at runtime, and it will need to know what is at compile time. Therefore, there is no way to pass an arbitrary array of Modules and have Dagger correctly running your schedule ; he defeats the runtime check and performance that the Dagger wrote to ensure.
However, you seem to be all right with one APK containing all possible implementations, so the only thing is to choose between them at runtime. This is very possible in a dagger and will likely fall into one of four solutions: a solution for David's dependency component -based, subclasses of modules, instances of state modules or @BindsInstance -based.
Component dependencies
Like David’s blog with which you are associated , you can define an interface with a set of bindings that you need to pass in, and then provide these bindings through the implementation of this interface passed to the constructor. Although the interface structure makes it well designed to implement Dagger @Component implementations in other Dagger @Component implementations, the interface can be implemented in any way.
However, I'm not sure if this solution suits you: this structure is also best suited for inheriting standalone implementations, and not in your case when your various WifiManager implementations have dependencies that your schedule should satisfy. You can turn to this type of solution if you need to support a "plug-in" architecture or if your Dagger graph is so huge that one graph does not have to contain all the classes in your application, but if you do not have these restrictions you can find this solution detailed and restrictive.
Subclasses of Modules
The dagger allows non- final modules and allows you to transfer instances to modules, so you can mimic the approach you have by passing subclasses of your modules into the Builder of your component. Since the possibility of replacing / overriding implementations is often associated with testing, this is described on the Dagger 2 testing page under the heading “Option 1: redefine bindings with subclass modules (don't do this!)” - this clearly describes the caveats of this approach, in particular, that calling the virtual method will be slower than the static @Provides method, and that any overridden @Provides methods will have to accept all the parameters that any implementation uses.
// Your base Module @Module public class WifiModule { @Provides WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) { /* abstract would be better, but abstract methods usually power * @Binds, @BindsOptionalOf, and other declarative methods, so * Dagger doesn't allow abstract @Provides methods. */ throw new UnsupportedOperationException(); } } // Your Samsung Wifi module @Module public class SamsungWifiModule { @Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) { return new SamsungWifiManager(dep1); // Dep2 unused } } // Your Huawei Wifi module @Module public class HuaweiWifiModule { @Override WifiManager provideWifiManager(Dep1 dep1, Dep2 dep2) { return new HuaweiWifiManager(dep1, dep2); } } // To create your Component YourAppComponent component = YourAppComponent.builder() .baseWifiModule(new SamsungWifiModule()) // or name it anything // via @Component.Builder .build();
This works as you can put one instance of a module and consider it as an abstract factory pattern , but by calling new unnecessarily, you are not using Dagger to its full potential. Also, having to maintain a complete list of all possible dependencies can make it more of a problem than it costs, especially considering that you want all the dependencies to be sent in one APK. (This may be an alternative with lighter weight if you need certain types of plugin architecture, or you want to avoid delivering the implementation entirely based on flags or compilation conditions).
Module Examples
The ability to provide a possibly-virtual module really meant more to pass instances of modules with constructor arguments, which can then be used to choose between implementations.
// Your NFC module @Module public class NfcModule { private final boolean useNfc60; public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; } @Override NfcManager provideNfcManager() { if (useNfc60) { return new Nfc60Manager(); } return new NfcDefaultManager(); } } // To create your Component YourAppComponent component = YourAppComponent.builder() .nfcModule(new NfcModule(true)) // again, customize with @Component.Builder .build();
Again, this does not use the dagger in full; You can do this by manually delegating the provider you need.
// Your NFC module @Module public class NfcModule { private final boolean useNfc60; public NfcModule(boolean useNfc60) { this.useNfc60 = useNfc60; } @Override NfcManager provideNfcManager( Provider<Nfc60Manager> nfc60Provider, Provider<NfcDefaultManager> nfcDefaultProvider) { if (useNfc60) { return nfc60Provider.get(); } return nfcDefaultProvider.get(); } }
It's better! Now you do not create any instances if you do not need them, and Nfc60Manager and NfcDefaultManager can take arbitrary parameters that Dagger provides. This leads to a fourth solution:
Paste Configuration
// Your NFC module @Module public abstract class NfcModule { @Provides static NfcManager provideNfcManager( YourConfiguration yourConfiguration, Provider<Nfc60Manager> nfc60Provider, Provider<NfcDefaultManager> nfcDefaultProvider) { if (yourConfiguration.useNfc60()) { return nfc60Provider.get(); } return nfcDefaultProvider.get(); } } // To create your Component YourAppComponent component = YourAppComponent.builder() // Use @Component.Builder and @BindsInstance to make this easy .yourConfiguration(getConfigFromBusinessLogic()) .build();
This way, you can encapsulate your business logic in your own configuration object, let the dagger provide the necessary methods, and return to abstract modules with static @Provides for better performance. In addition, you do not need to use Dagger @Module instances for your API, which hides implementation details and makes it easier to switch from a dagger later if your needs change. For your case, I recommend this solution; this will require some restructuring, but I think you will have a clearer structure.
Guice Module # configure (Binder) Side Note
This is not idiomatic for calling the function.configure feature.configure(binder()) function; use install(feature); instead. This allows Guice to better describe where errors occur in your code, detect @Provides methods in your modules, and @Provides instances of your module in case the module is installed more than once.