MaaAn13
MaaAn13

Reputation: 258

Casual constructor injection vs dependency injection frameworks

Okay, so I decided to take a closer look on implementing junit tests in my project. Then I stumbled on an article that writes about how important it is to use dependency injection frameworks such as Dagger2 and Koin to ease testing.

I tried to read - What exactly is DI. I understand it as a way of resolving Class A dependant objects (B class and C class, for instance).

What I do right now usually is this:

In Activity I create ViewModel. ViewModel needs to access data, so for data I have SomeRepository class. Then I usually pass SomeRepository through ViewModel constructor or using property injection. As I understand, that is also some type of dependency injection if I'm not mistaken (correct me if I'm wrong).

So, what benefits I'd gain If I'd start using Dagger 2 now? Maybe simple code comparison would make it clearer? Thanks in advance.

Activity:

val someRepository = SomeRepository()
viewModel.init(someRepository) 

In ViewModel:

class SomeViewModel : ViewModel {

    private lateinit var repository: SomeRepository

    fun init(val someRepo: SomeRepository) {
        this.repository = someRepo
    }

}

Upvotes: 4

Views: 234

Answers (2)

Rishabh Jain
Rishabh Jain

Reputation: 297

Pro1: In layman's terms, DI is important to write decoupled code that makes it easy to write unit tests.

Pro2: It also abstracts dependencies of the object from the parent class. For eg, in your use case, Activity is not directly using SomeRepository but still needs to know about it. But instead, if you used constructor injection like so:

class SomeViewModel constructor (@Inject someRepository: SomeRepository) : ViewModel {
}

your activity class just needs to deal with the SomeViewModel object and any dependencies used by SomeViewModel are none of its concern.

Pro3: DI makes it very easy to create and access singleton objects. Just define a class like below and you can get a single instance of the MySingleton class anywhere in your app.

@Singleton
class MySingleton @Inject constructor() {}

OR

@Provides @Singleton mySingleton() {}

Upvotes: 1

Prokash Sarkar
Prokash Sarkar

Reputation: 11873

Dagger promotes decoupling of dependencies with it's own dependency graph. We need to tell dagger how the classes can be generated e.g. it takes a constructor parameter. Once defined, it travels through all the dependencies and starts creating them from the low level. If we can't inject any dependency in the constructor e.g. Retrofit we can manually tell dagger how to create an instance of if with @Provides annotation.

Let's see an example:

Dependency #1 - Invididual moduels

@Module
class NetworkModule {

 @Provides
 fun provideRetrofit() : Retrofit  

}

Dependency #2 - Depends on Dependency #1

class ApiService constructor (@Inject retrofit: Retrofit){
}

Dependency #3 - Depends on Dependency #2

class SomeRepository constructor (@Inject apiService: ApiService){
}

Dependency #4 - Depends on Dependency #3

class SomeViewModel constructor (@Inject someRepository: SomeRepository) : ViewModel {
}

Dagger will generate all dependencies in the runtime. When you will run the app, each dependency is ready and can be injected directly in constructor. You are also allowed to Inject any of those dependencies with field injection

class MainAcitivty : AppCompatActivity(){

 @Inject someRepository: SomeRepository

}

Cons with your current approact:

val someRepository = SomeRepository()
viewModel.init(someRepository) 
  1. You will create a new instance of SomeRepository() over and over again in different acitivies
  2. If SomeRepository() has any dependency those are also going to created several times
  3. You are making tight coupling. It will be very difficult to test the SomeRepository() without its dependencies. In unit test, we are interested in testing the unit not it's dependencies.

Upvotes: 5

Related Questions