At this point, my understanding of Dependency Injection (DI) is only from this article. I'm interested to try, but I just need to clarify some things:
- Many refer DI as a tool to reduce boilerplate code. But according to that tutorial, the setup of Dagger 2 tends to create even more classes for configuration (module and component). I have not tried it, but by the looks of it, it isn't reducing the code, it is just splitting them so the main class can look tidier. Am I wrong on this one?
- Despite Dagger 2's claim that DI isn't just for testing, many are regarding its usage mainly for testing, including Android's own guide. Have you used Dagger 2 in production-ready app? How useful has it been to you?
- If I am perfectly comfortable with traditional dependency creation via constructor and such, should I still need to look at Dagger 2? I feel like this library might have the power to change how I code the way RxJava does, I'm just not sure the benefit of it is as much as RxJava gave me.
I know, comparing Dagger to RxJava is like comparing apple to orange. But in a sense, they are both a fruit just like Dagger and RxJava are third-party libraries that might make my project larger.
You've combined two separate questions, which should be evaluated separately: "Why do we need dependency injection?" and "why do we need Dagger 2?"
Dependency injection (inversion of control) is useful because it allows the consumer of your component to provide your component's dependencies. Take a log formatter, for example: Without dependency injection you might need to write three different versions of your class that log to stdout
, a remote server, or a text file. By comparison, if you were to write a LogFormatter
that accepts a Writer
to which it writes, then you could write it once and pass in whichever Writer
is most appropriate, including a test double (a FakeWriter you make, or a StringWriter, or a mock-framework-created mockWriter instance) for testing. Though it's written for Guice instead of Dagger, I wrote a separate SO answer that talks about the value of dependency injection in production usage and also test cases; most of the tutorials you see will focus on tests under the presumption that "production" and "test" are the two cases you know about up front, where other opportunities for reuse and repurposing will present themselves later.
Once you embrace the modularity, reusability, and testability benefits that dependency injection provides you, you'll probably be left with one question: How do I manage these enormously-long constructors? After all, to carry forward the LogFormatter
example, you wouldn't be able to write your Application without caring where the logs go to.
MyApplication application = new MyApplication(
new LoggingService(new LogFormatter(new StdOutWriter())),
new DatabaseService(new MyApplicationDatabase(new File("my.db"))),
...);
This is where Dagger shines: It lets you have all the benefits of dependency injection while automatically managing the constructors for you. This allows it to encapsulate the responsibility of creating objects and make it cleaner and safer, the way that RxJava can encapsulate the responsibility of managing and propagating asynchronous events and make it cleaner and safer.
It's important to realize that Dagger 2's reduction in boilerplate is in comparison to manual dependency injection, not the raw constructor invocations that you're comparing to. If you stick with calling new
directly, you'll probably avoid much of this object construction boilerplate entirely, but you'll also find yourself going through difficult acrobatics trying to shard the work out to multiple developers or trying to test or reuse the code you've written. The collective pain is why dependency injection is so popular of a concept, and why libraries like Spring, Guice, and Dagger are gaining popularity.
I can vouch for the use of Dagger 2 in several particularly large, well-known, and well-used production Android applications. :)
I have only used Dagger 1 but this might still help you in some way or another:
Dagger builds up a (D)irected (a)cyclic (g)raph (thats also where i the name comes from) with modules for different target applications.
So within one app project we were able to build four different apps, pretty similiar to Jake Wharton's u2020 app. All these apps had different targets. One was internal testing, one was for the testing team (with a screenshot function), one was internal release and the other one was release.
So why do we need four different apps here? The answer is: So we don`t have to have debug or test code in our release application and we can separate code that does not belong together.
if(currentMode == Mode.DEBUG){
Timber.i(...);
}
Something like that is not necessary any more.
The screenshot functionality mentioned earlier, does not belong in the production code, so it was only in the testing code. When building an app, the framework used the correct module and injected it.
Note: I am sure there are other use cases for dagger than that.
EDIT: This will not change how you write code in a way RxJava does, but you have to use DI a lot in your modules.