Skip to main content

Dependency Injection - Overview

Advantages of DI

  • Reusability of code
  • Ease of refactoring
  • Ease of testing

For an Example, Car class and Engine class. A Car should have an object of Engine. How to achieve it ?

1. By creating an Engine object in Car class  <-- This provides the tight coupling of Engine object in the Car class. This approach makes testing difficult.

2. By creating a global class from where we can access the Engine object in the way we access getSystemService(), getContext() etc. <-- This is called service locator pattern. As it is global place to access all objects, the testing will still become difficult. 

3. By passing the object of Engine object when constructing the Car class <-- This is called Dependency Injection


An Example of Car tightly coupled with Engine

class Car {
private val engine = Engine()
fun start() {
engine.start()
}
}


class ClassA {
fun main(Array arr){
var car: Car()
car.start()
}
}

Drawback

 Let's consider a scenario, the Engine can have multiple implementations like GasEngine, ElectricEngine etc. 

   

  ->  Approach 1

class Car {
private val electricEngine = ElectricEngine()
private val gasEngine = GasEngine()

fun startElectricEngine() {
electricEngine.start()
}

fun startGasEngine() {
gasEngine.start()
}
}


class ClassA {
var car: Car()
car.startElectricEngine()
car.startGasEngine()
}

   

  -> Approach 2

  

class ElectricEngineCar{
private val engine = ElectricEngine()

fun start(){
engine.start()
}
}


class GasEngineCar{
private val engine = GasEngine()
fun start(){
engine.start()
}
}

class ClassA {
var electricCar: ElectricEngineCar()
var gasEngineCar: GasEngineCar()
electricCar.start()
gasEngineCar.start()
}

With the above two approaches, the boilerplate code is increased and it is not recommended. For enabling the re-usability of code, we can think about service locator but it is a bit complex when it comes to testing. So, We consider Dependency Injection.

Types of DI 

1. Manual Dependency Injection

2. Automated Dependency Injection


Manual Dependency Injection

Two types

 --> Constructor Injection

 --> Field Injection


Constructor Injection - Example

class SongManager(private val song: Song){
fun play(){
song.play()
}
}

class ClassA {
fun main(Array arr){
var song = ClassicalSong()
var songManager: SongManager(song)
songManager.play()
}
}


class ClassB {
fun main(Array arr){
var song = FolkSong()
var songManager: SongManager(song)
songManager.play()
}
}

In Android, We usually create the Activities/Fragments without constructor as instantiation part will be taken care by system itself. So, Constructor Dependency Injection is not possible.

In this scenario we can use Field Injection


Field Injection - Example

class SongManager {
lateinit var song: Song
fun play() {
song.play()
}

}

class ClassA {
fun main(Array arr){
var songManager: SongManager()
songManager.song = ClassicalSong()
songManager.play()
}
}


class ClassB {
fun main(Array arr){
var songManager: SongManager()
songManager.song = FolkSong()
songManager.play()
}
}

Drawbacks of Manual Dependency Injection

-- > For the larger application, it is difficult to manage the multiple constructors

--> In case if it is lazy field initialization,we have to have a create a separate container to manage the lifetime of objects in the memory.

To overcome this, we go for Automated Dependency Injection

Automated Dependency Injection

1. Reflection-based solution 

  --> To connect dependencies at the run time. There is a drawback. It may cause the performance issues as it is Reflection-based.

  --> Library -  Dagger(Created by Square and Maintained by Google)

2. Static solution 

  --> To generate code for connecting dependencies at the compile time. It's pretty good performance as compared to Reflection-based(https://stackoverflow.com/questions/435553/java-reflection-performance)

  --> Library - Guice(Created and Maintained by Google)

Using Hilt 

Hilt is the recommended Jetpack library in Android for enabling the Android lifecycle awareness. To provide all the support that Dagger can, it is built on top of Dagger.


Reference : https://developer.android.com/training/dependency-injection#kotlin


Comments

Popular posts from this blog

Circular Seek Bar - Example

MyView.java package com.rakesh.androidcircularseekbar; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class MyView extends View {     /** The context */     private Context mContext;     /** The listener to listen for changes */     private OnSeekChangeListener mListener;     /** The color of the progress ring */     private Paint circleColor;     /** the color of the inside circle. Acts as background color */     private Paint innerColor;     /** The progress circle ring background */     private Paint circleRing;   ...

SSL pinning in Android - A brief discussion

HTTP protocol Communication between the client and a server typically non-encrypted or plain text while we use HTTP protocol.  Pitfall Any middle hacker can interrupt the connection between the client and server and manipulate the data as it involves no encryption. How to overcome this ? As the domain owner one can purchase a digital certificate from CA(Certificate Authority) who are considered as trusted.  A certificate will contain the Owner's name, public key , Issuer's(CA's) name,Issuer's(CA's) signature, domain details, expiry date etc . After the SSL/Leaf certificate is associated with a domain,the communication between client and server will be encrypted. Now the HTTP will become HTTPs. Note : Associating the SSL certificate means it enable the encryption between client and server but does not mean ,the domain owner will never misuse your personal information. How does SSL work ? Pitfall There is a problem here. Let's assume that there is a hacker comes i...

Android - Activity Life cycle

The android activity provides 7 call backs which will be invoked based on the states of the activity For better undersatnding,The Activity A and Activity B with the all the call backs overridden. ActivityA :  public class ActivityA extends Activity {     @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         Log.e("A-OnCreate", "Object Creation");     }     public void next(View v) {         Intent i = new Intent(this, Main2Activity.class);         startActivity(i);     }     @Override    protected void onStart() {         super.onStart();         Log.e("A-onStart", "View is visible but will not be able to interact");     } ...