18

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:

  1. 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?
  2. 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?
  3. 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.

Hendra Anggrian
  • 5,181
  • 12
  • 51
  • 93
  • if you only read 1 article about DI - http://stackoverflow.com/questions/130794/what-is-dependency-injection might be interesting for you – Redlab Jan 27 '17 at 14:15
  • To see whether Dagger is being used in "production ready apps" take a look at [AppBrain](http://www.appbrain.com/stats/libraries/details/dagger/dagger). While this stat may include the similar Dagger 1, you can see that altogether it's used in > 2% of apps and > 12% of installs – David Rawson Jan 27 '17 at 20:17

2 Answers2

13

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. :)

Community
  • 1
  • 1
Jeff Bowman
  • 74,544
  • 12
  • 183
  • 213
  • but in terms of testing we can get away without it and just use mockito to mock MVP presenter dependencies right ? i do see the use in inversion of control though. am i correct ? – j2emanue Aug 06 '19 at 08:22
  • @j2emanue Yes, you can stop at inversion of control and use Mockito for your unit tests. However, you will be left managing deep trees of constructor calls, which will likely vary between production and test builds, and your development builds may vary too. You might choose to do this, depending on how many builds and classes you have to manage, but by the time you hit a certain size/complexity you are likely to need a framework, and Dagger 2 has historically been hard to beat for optimized Android code. – Jeff Bowman Aug 06 '19 at 10:51
  • @j2emanue Unless your question was "should I use Dagger in tests as well as production". Unit tests no, integration tests yes. – Jeff Bowman Aug 06 '19 at 10:54
  • in integration test i like them to be as real as possible. so real DB connection, real network layer connection etc. i was searching for an article on why we would use dagger in a integration tests. if you know any do let me know. – j2emanue Aug 06 '19 at 11:32
  • @j2emanue You would use it in integration tests if your app needs to vary slightly: Real DB but in-memory backend, maybe, or a deterministic implementation of what is usually a random number source. If you only ever really need your production graph from integration tests, all the better, but I think it's common for them to vary slightly. – Jeff Bowman Aug 06 '19 at 11:38
1

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.

Stefan Lindner
  • 293
  • 10
  • 18