Replaceable modules with a dagger 2

So, I have code that runs an algorithm, say, with the AlgoRunner class. Now this AlgoRunner class can be implemented in several ways to run various algorithms using Algo classes. I want to use Dagger 2 to provide various implementations of the AlgoRunner class to the "Manager" class, which passes input to AlgoRunner, as well as other components that it manages.

Question

I have the following right now, but I'm not sure if this is the right way, mainly because of this empty AlgoRunnerProvider module. Is this some other way to achieve what I'm trying to do? Or simplify what I have?

Should I just create different components, OneAlgoRunnerComponent and TwoAlgoRunnerComponent and introduce a Manager from each of them?

The class that the Manager instance creates uses this component to enter AlgoRunner into this instance so that the Manager can pass inputs to it.

@Component( modules = { AlgoRunnerProvider.class } ) public interface AlgoRunnerComponent { void inject(Manager manager); AlgoRunner getAlgoRunner(); } 

AlgoRunnerProvider Module

 @Module public class AlgoRunnerProvider { @Provides public AlgoRunner getAlgoRunner() { return null; } } 

OneAlgoRunnerProvider, which overrides the rendering method in AlgoRunnerProvider. It may also have a bilingual provider that does the same and provides TwoAlgoRunner if it extends AlgoRunner.

 public class OneAlgoRunnerProvider extends AlgoRunnerProvider { private final OneAlgo algo; public OneAlgoRunnerProvider(OneAlgo algo) { this.algo = algo; } @Override public OneAlgoRunner getAlgoRunner() { return new OneAlgoRunner(algo); } } 

All of this is being used right now:

 AlgoRunnerComponent build = DaggerAlgoRunnerComponent.builder() .algoRunnerProvider(new OneAlgoRunnerProvider(new OneAlgo())) // .algoRunnerProvider(new TwoAlgoRunnerProvider(new TwoAlgo())) .build(); Manager manager = managerComponent.getManager(); build.inject(manager); Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class); // Truth.assertThat(manager.algoRunner).isInstanceOf(OneAlgoRunner.class); 

Thank you so much!

+1
java android dagger-2
source share
1 answer

The dagger frame is used to handle the creation of objects for you. If you start some kind of initialization in one of your classes that you want to provide, maybe something is not as it should be (see getAlgoRunner() ).

If you have different types that you want to provide at runtime, you want the factory to create the correct object. Enter the dagger.

You have several ways to achieve what you want. Basically, the module should handle the creation of the object:

 @Module public class AlgoRunnerProvider { @Provides public AlgoRunner getAlgoRunner() { // todo create the correct type return null; } } 

1. @Named annotation (or some other Qualifier )

If you know at compile time which class will be needed, which type, you should use qualifiers.

 @Named("Version1") @Inject AlgoRunner mRunner; 

Then you can simply provide various implementations from your module:

 @Provides @Named("Version1") public AlgoRunner getAlgoRunner() { return new Version1AlgoRunner(); } @Provides @Named("OtherVersion") public AlgoRunner getAlgoRunner(Depends someOtherDependency) { return new OtherVersionAlgoRunner(someOtherDependency); } 

2. Switching at runtime

Although you can always use the first option by creating several classes with different dependencies, you might want to choose at runtime. To do this, you need to pass some kind of argument to your module:

 @Module public class AlgoRunnerProvider { private final int mType; public AlgoRunnerProvider(int type) { mType = type; } @Provides public AlgoRunner getAlgoRunner() { if(mType == TYPE_A) { return new Version1AlgoRunner(); } else { return new OtherVersionAlgoRunner(); } } } 

In this option, you still have the logic of creating inside your module where your dependencies come from.


3. Use different modules

Another approach would be to use different modules. This will be a pure solution if it is defined at compile time (different classes using different modules), rather than choosing logic at run time in the same class.

If you start writing code, for example if typeA then moduleA else moduleB , you should stop and do something else.

You can use the same component, but create it using different modules for different classes, using good old inheritance. Each module simply provides an implementation of AlgoRunner .

 // one class using one module to get a specific behavior public class MyClassVersionA { public void onCreate(Bundle saved) { component.myModule(new MyModuleVersionA()).build(); } } // another class using a different module to get a different behavior public class MyClassSomethingElse { public void onCreate(Bundle saved) { component.myModule(new MyModuleSomethingElse()).build(); } } 

Then you would just subclass your module accordingly like this

 // did not test the following, something like this... public abstract class MyModule { @Provides public AlgoRunner getAlgoRunner(); } public class MyModuleVersionA extends MyModule { @Provides @Override public AlgoRunner getAlgoRunner() { return new Version1AlgoRunner(); } } public class MyModuleSomethingElse extends MyModule { @Provides @Override public AlgoRunner getAlgoRunner() { return new SomeOtherAlgoRunner(); } } 

There are probably even more options, especially if you are starting to mix these approaches, but I think these 3 will be the main tools you can use.

+1
source share

All Articles