Dependency Injection: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
Line 171: Line 171:
     }
     }
...
...
</syntaxhighlight>
And in the remote class
<syntaxhighlight lang="kotlin">
class Remote @Inject constructor() {
    private const val TAG = "Remote"
   
    fun setListener(Car car) {
        Log.d(TAG, "Remote connected")
    }
}
</syntaxhighlight>
</syntaxhighlight>


==Kotlin vs Java==
==Kotlin vs Java==
On Java the interface fails to compile with Missing/Binding however on Kotlin this is allowed. Clearly it would be ideal to identify issues in both.
On Java the interface fails to compile with Missing/Binding however on Kotlin this is allowed. Clearly it would be ideal to identify issues in both.

Revision as of 03:46, 1 February 2021

Introduction

Dependency Injection or DI is when we provides the things we need into another object.

Originally in OO is was thought better to hide the internal objects and create them inside the object.

class Car {
   Engine engine
   Wheels wheels;

   Car() {
      engine = new Engine()
      wheels = new Wheels()
   }

   void drive() {
     // chug chug
   }

}


For Dependency Injection we can provide the prebuilt wheels and engine via the constructor

class Car {
   Engine engine
   Wheels wheels;

   Car(Engine engine, Wheels wheels) {
       this.engine = engine
       this.wheels = wheels
   }

   void drive() {
     // chug chug
   }

}

Why Dagger?

So we taking our example above we can now do.

..
   val engine = new Engine()
   val wheels = new Wheels()
    
   val car = new Car(engine, wheels)
..


Looks simple enough? Well lets add a few more parts

..

   val block = new Block()
   val cylinder = new Cylinder()
   val rims = new Rims()

   val engine = new Engine(block, cylinder)
   val wheels = new Wheels(rims)
    
   val car = new Car(engine, wheels)
..


Dagger exists to help manage the dependencies in terms of

  • dependencies
  • ordering
  • construction

With dagger this becomes

   val carComponent = DaggerCarComponent.create()
   val car = component.getCar()

So here we have a Directed Acyclic Graph' of our car or DAG :)

Initial Project

Gradle Considerations

For both we needed

    implementation 'com.google.dagger:dagger:2.31.2'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.31.2'

However for kotlin we also needed

plugins {
...
    id 'kotlin-kapt'
}

// Dependencies
    kapt 'com.google.dagger:dagger-compiler:2.31.2'

What is Reqired

We need to

  • Specify @Component interface
  • Specify @Inject on the class to inject and its dependants

This compiles and creates our factory (builder) which is Dagger<Interface name>, in this case DaggerCarComponent. From there we can call getCar() to get our instance.

Constuctor Injection

So we can now put the theory into practice.Here is our Car class.

class Car @Inject  constructor(private var engine: Engine, private var wheels: Wheels) {

   private const val TAG = "Car"

   fun drive() {
       Log.d(TAG, "driving...")
   }
}

And the Wheel and Engine

class Wheels @Inject  constructor()
class Engine @Inject  constructor()

And an interface to allow us to get the Car

@Component
interface CarComponent {

    fun getCar() : Car
}

Now we can create the Car with

...
    private lateinit var car: Car
...

        val component: CarComponent = DaggerCarComponent.create()
        car = component.getCar()
        car.drive()

Field Injection

For a class, in our case MainActivity, we can inject fields, so we can pass a class and have its field created. To do this all we need to do is

  • Create a function on the component interface
  • Add @Inject to the fields
  • Create the component and
  • Call the interface function
@Component
interface CarComponent {

    fun inject(activity:MainActivity)
}

And in the MainActivity

class ...
    @Inject lateinit var car: Car
...
        val component: CarComponent = DaggerCarComponent.create()
        component.inject(this)
        car.drive()
...

Method Injection

We can inject into methods as well but this is not very component. An example might be when you are passing the yourself to a method argument. e.g.

class Car ...
    @Inject 
    fun enableRemote(remote: Remote) {
        remote.setListener(this)
    }
...

And in the remote class

class Remote @Inject constructor() {

    private const val TAG = "Remote"
    
    fun setListener(Car car) {
        Log.d(TAG, "Remote connected")
    }
}

Kotlin vs Java

On Java the interface fails to compile with Missing/Binding however on Kotlin this is allowed. Clearly it would be ideal to identify issues in both.