Dagger 2 for Android Beginners — DI part I
This story is the second part of the series, Dagger 2 for Android Beginners. If you did not read the previous one, you can start from here.
Series Pit Stops
- Dagger 2 for Android Beginners — Introduction
- Dagger 2 for Android Beginners — DI part I (you’re here)
- Dagger 2 for Android Beginners — DI part II
- Dagger 2 for Android Beginners — Dagger 2 part I
- Dagger 2 for Android Beginners — Dagger 2 part II
- Dagger 2 for Android Beginners — Dagger 2 Advanced part I
- Dagger 2 for Android Beginners — Dagger 2 Advanced part II
Previously on Dagger 2..
In our last pit stop, we had our discussion on dependencies. We learned — what’s meant by dependency, its types and how they affect our software development and code maintenance.
Prerequisites
I assume that you’re familiar with the understanding of dependency in programming(previous pit stop), Java Programming language, OOP Concepts and Android development.
Note: If you’re not comfortable with the way of my storytelling — keeping Game of Thrones as the concept, feel free to change the name of the classes when you proceed.
What is dependency injection?
Previously we understood the meaning of dependency and its effects on our project. Now we’ll understand the meaning of dependency injection.
Before we jump to DI, we need figure out the solution — to avoid getting into the trap laid by the dependencies. In other words, hard dependency problems (non-reusable and testable code) are like White Walkers, we should not be joining their army — instead, we need to figure out a way to defeat them.
Strategy to solve the hard dependency problems a.k.a white walkers
We need a solid plan to tackle or avoid/defeat the hard dependency issues. This strategy or the plan is called as Dependency Injection (DI). In other words, to kill the white walkers, you can either burn them or use dragon glass. Similarly to avoid hard dependencies, you need to use or follow dependency injection technique.
DI is just one of the technique to avoid dependencies. There are many other ways as well. For Android development, this method is considered little easy and many of the large-scale production apps use this strategy.
Dependency Injection Technique
So, what’s this DI technique?
Dependency injection is a technique whereby one object supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.(source)
When we compare this to GOT, Cersei is getting prepared for the big war. Instead of burning all the cash in her kingdom, she is trying to get a loan from the iron bank of Braavos. Here, the cash is the dependency — needed by all the houses (houses depends on cash for war). So an external object(iron bank) will inject the dependency(cash) to the dependants(houses).
In other words, Dependency Injection in build upon the concept of Inversion of Control. Which says that a class should get its dependencies from outside. In simple words, no class should instantiate another class but should get the instances from a configuration class.
Example 1
Enough of talking.. let’s dive into some code. Now we’re going to see a small example where we’ll see two scenarios. First, we’ll create few classes with hard dependencies(without using DI). Then we’ll follow dependency injection and decouple the things as planned.
Scenario 1: Without dependency injection
Problem: Battle of Bastards— Starks and Boltons are preparing for the war to take over the North. We need to prep them and bring them to the war.
Since our examples will/may involve a lot of the houses, let’s create a common interface called House
and make all other houses implement it. In our interface House
I’ve included a couple of methods — prepareForWar()
and reportForWar()
(Ignore the childish functions :-P).
Next, we need to create classes for our two houses — Starks
and Boltons
. Let’s create them and implement the interface House
.
Note: this.getClass().getSimpleName()
will just return the class name
Next, we need to bring both of these houses to the war. Let’s create the class War
and request them to get prepared and report on time
Analysing War
Let’s consider the constructor of the class War
— which needs the other classes (all the houses), here we have 2 classes Starks
and Boltons
. These two classes get prepared and report to the war.
In a well designed object-oriented application, each object has only a very small number of responsibilities, and rely on other objects to accomplish most of the work. Here the War
classes depend upon Starks
and Boltons
. So these are the dependencies of the Class War
. Without these, the War
will not happen. Before an object starts doing real work, all of its dependencies must be satisfied somehow. The dependencies for our War
, are satisfied by creating new instances of them in its constructor.
While instantiating dependencies on the constructor works well enough for small applications, it comes with a number of shortcomings as the application evolves. First, it makes the class rather inflexible. If this app is supposed to run on multiple platforms, for example, we might want to swap Boltons
with another, but this cannot be easily done. Perhaps, we would also like to share the same Boltons
among multiple objects, but this is also hard to accomplish unless the class is modified.
Secondly, it is impossible to test our class in isolation. Constructing a single War
will always automatically construct two other objects, which will end up getting tested along with the original object. This can be a serious problem if one of these dependencies relies on an expensive external resource such as Allies
, or if it has a large number of dependencies itself.
As mentioned earlier, hard dependencies are like fighting White Walkers without a proper weapon.
TL;DR
In this pit stop, we discussed dependency and dependency injection in detail -no class should instantiate another class but should get the instances from a configuration class.
Again, dependency injection is a method to solve or avoid hard dependency problem, by providing the dependencies needed for the class from an external source — there by class becomes independent, easily testable and readable.
We also saw an example of a hard dependency problem, creating the battle of bastards scenario.
What’s Next?
On the next pit stop, we’ll decouple the War class using Dependency Injection — using the traditional approach.
Plus, check out my other stories.
Recommended Reading
Thank you for using your precious time to read this article. Liked it? Clap your 👏 to say “thanks!” and please share to help others find this article.