— Dependency Injection, Android, Dagger — 3 min read
You might have heard of dependency injection. It’s been one of the hot topics in the Android community for the past few years . So you look around on the internet, find articles about Dagger and are lost when people start mentioning Modules, Components, and Scopes. That is because those articles don’t do a very good job of explaining what is dependency injection (Thermosiphon anyone?) & why you need it.
Before diving into such articles, you must have a clear understanding of why are you using dependency injection (DI) and what benefits does it provide you. As the Principle of Applying Principles states, “The good and proper application of each principle, pattern and practice requires you to understand why you are doing what you’re doing”. So this article will try to answer the following questions :
As you can infer from its English meaning, the word dependency indicates that a part of code depends on some other part of code. It could be either loose coupling (where the code depends on an abstract class or interface) or strong coupling (where the code depends on a concrete class). I believe examples provide a better understanding so lets see an example.
1Class A {23 public int doSomeWork() {4 B objectB = new B();5 int result = objectB.getResult();6 int finalResult = result * 2;7 return finalResult;8 }910}
The function doSomeWork
uses some functionality from class B. So class A has a dependency on class B. If class B required some constructor parameters, it would become the responsibility of doSomeWork
to provide those parameters.
1class A {23 public int doSomeWork(int p1, int p2, String p3) {4 B objectB = new B(p1, p2, p3);5 int result = objectB.getResult();6 int finalResult = result * 2;7 return finalResult;8 }910}
If the function depended on more objects each of which required multiple constructor parameters, the function doSomeWork
would become too unwieldy. Instead of just doing the specific thing it was meant to do, it would also contain code for creation of the needed objects. As you can see, it is not really wise to write our code this way. Now onto our second question, what is dependency injection.
The English language to our rescue again! Dependency injection is the act of injecting dependencies. Well, injection is just a fancy word for providing. So dependency injection is the act of providing dependencies.
Hence, instead of this,
1class A {23 public int doSomeWork(int p1, int p2, String p3) {4 B objectB = new B(p1, p2, p3);5 int result = objectB.getResult();6 int finalResult = result * 2;7 return finalResult;8 }910}
we could have this
1class A {23 private B objectB;45 public A(B objectB) {6 this.objectB = objectB;7 }89 public int doSomeWork() {10 int result = objectB.getResult();11 int finalResult = result * 2;12 return finalResult;13 }1415}
That’s it! You just performed dependency injection. The dependency (Class B) was injected (as a constructor parameter) into the code which needed it (Class A). All the fancy DI frameworks and libraries try to help you in making your code less like snippet_3 and more like snippet_4. Well, why would you want to do that? Let’s move on to our next section to find out.
1) Reducing responsibility : In code snippet_4, the function doSomeWork
is not responsible for the creation objectB
. It does not care how objectB
is provided or who provides it. It’s only responsibility is to ‘do some work’, and the fewer responsibilities a piece of code has, the less error prone it is.
2) Easier testing : In code snippet_3, doSomeWork
is explicitly responsible for creating the objectB
. For testing, the creation of object should be done outside the code to be tested. As an example, we could pass a fake objectB
in snippet_4 to test doSomeWork
.
3) We don’t need to change unrelated code : In future, if we change B (say, we add a constructor parameter), it will require us to change doSomeWork
too. If we had initialized B in 10 places throughout our code, it would have required us to change the code in 10 different places If we use dependency injection, we will just modify the part where we initialized B.
4) Improved code reusability : If you use dependency injection, you just need to initialize B just once. You can then just pass that object to whatever part of code that requires it. This makes our code more reusable.
For a more concrete tutorial about dependency injection on Android, check out this playlist by TwistedEquations. You can see how creating each dependency manually can get quickly get out of hand.
I would recommend you to stay away from Dagger Android till you get comfortable with @Module, @Provides, @Inject and @Component. Although it introduces a lot of stuff to reduce the boilerplate required for Dagger, you need to have a clear understanding of the aforementioned concepts before you try it. Learning Dagger and Dagger Android together would be too much for a beginner.
So that’s it. I have tried keeping this article simple and not use any of the technical terms usually used while explaining dependency injection. Thanks for reading my first article!
This article was originally published on Medium