I was kind of lazy and used to use almost entirely field injections. I was just providing empty constructor, put my @Inject fields I everything was looking nice and simple. However field injection have its trade-offs so I've devised some simple rules that help me to decide when to used field and when to use constructor injections. I will appreciate any feedback if there is mistake in my logic or if you have additional considerations to add.
First some clarification in order to be on the same page:
Constructor injection:
@Inject public SomeClass(@Named("app version") String appVersion, AppPrefs appPrefs) {...
Same with the field injection:
public class SomeClass { @Inject @Named("app version") String mAppVersion; @Inject AppPrefs appPrefs;
Rule 1: MUST use field injection if I don't control creation of the object (think Activity or Fragment in Android). If some (non-dagger aware) framework is creating my object and handles it to me I have no choice but to inject it manually after I receive the instance.
Rule 2: MUST use constructor injection if the class is/may be used in another project that does not use Dagger 2. If the other project(s) do not use Dagger they cannot use DI so the user have to create the object the "old" way using new
.
Rule 3: PREFER constructor injection when working with class hierarchies because it is easier to create unit tests.
Clarification:
Considering the following structure that uses field injection:
package superclass; public class SuperClass { @Inject HttpClient mHttpClient; ... }
.
package differentpackage; public class SubClass extends SuperClass { public SubClass() { } }
When I am creating unit test for SubClass
in directory test/java/differentpackage
I have no choice but to bring up the entire DI infrastructure in order to be able to inject the HttpClient
. In contrast, if I was using constructor injection like this:
public class SuperClass { private final HttpClient mHttpClient; @Inject public SuperClass(HttpClient httpClient) { mHttpClient = httpClient; } }
in my unit test I could simply:
HttpClient mockHttp = mock(HttpClient.class); Subclass tested = new Subclass(mockHttp); // tests
So basically now I am in the other extreme: I tend to rely mostly on constructor injections and use field injections only when 'Rule 1' applies. The only 'problem' that I have with the constructor injects is that for 'end' classes constructors sometimes become quite overloaded with parameters and they look verbose and ugly like this:
@Inject public ModelMainImpl(@Named("app version") String appVersion, AppPrefs appPrefs, LoginPrefs loginPrefs, @ForApplication Context appContext, NetworkInfoProvider networkInfoProvider, AndroidEventPoster androidEventPoster, Session session, ForgeExchangeManager exchangeManager, HttpFunctionality httpFunctionality, @Named("base url") String baseUrl, @Named("forge result producer") ResultProducer<ForgeExchangeResult> resultProducer ) {
Guys, what are your rules to choose between constructor and field injects? I am missing something, are there errors in my logic?
We can add the @Inject annotation to the constructor, then Dagger will use this constructor to create instances when we need it. Now Dagger knows how to create a Pizza object, so we can inject it into any places by annotating @Inject to the field.
inject. Inject annotation to identify which constructors and fields it is interested in. Use @Inject to annotate the constructor that Dagger should use to create instances of a class. When a new instance is requested, Dagger will obtain the required parameters values and invoke this constructor.
Constructor injection helps in creating immutable objects because a constructor's signature is the only possible way to create objects. Once we create a bean, we cannot alter its dependencies anymore.
CONCEPTS. Field injection is when Dagger sets an @Inject -annotated field on a class. Method injection* is when Dagger calls an @Inject -annotated method on a class. Note that @Inject -annotated fields and methods may not be private or static , and fields may not be final .
Use constructor injection. if you can't, use property injection.
Rule 1 seems ok, like decorations or attributes you can use Property(field) injection.
Rule 2 seems ok, because who uses your class they have to follow your constructor. They may not know they have to intilaize your property also.
Rule 3 It's not just good for unit test. It's good for applying Single Responsibilty. It's easier to see your object graph.Otherwise you will hide it with property.
If we come in your question, yes there a lot of parameters in your constructor. But the solution is not property injection. You can refactor your code and use aggregate services
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With