A Guide to Dependency Injection

Dependency Injection? What the heck is that?

Dependency Injection (DI) is a software engineering technique often used in object-oriented software development to handle object relationships. Rather than having an object create or manage its…


This content originally appeared on DEV Community and was authored by David Rufai

Dependency injection image

Dependency Injection? What the heck is that?

Dependency Injection (DI) is a software engineering technique often used in object-oriented software development to handle object relationships. Rather than having an object create or manage its required components (dependencies), DI involves providing these components from an external source. This approach is like giving an object the tools it needs instead of letting it gather them. By implementing DI, developers can create systems where different parts are less tightly interconnected. This results in easier to modify, test, and maintain applications over time. It also allows seamless flexibility in swapping out components without affecting the overall system structure.

Here's a very basic example in kotlin.

 class GenericEngine() {
   fun start() {
     print("engine started")
   }
 }


class Car() {   
   val engine = GenericEngine()

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

Here, the Car is responsible for creating its engine, which seems pretty normal for now, right? But what happens when we want to build a car with a different type of engine? Do we create a different car class and then define our custom engine in it, or just change the GenericEngine instance to a different engine instance? This approach has some issues: the former being having to write more code for just a slightly different behavior, and the latter being having to modify a field which could cause breaking changes. This could go south quickly in large-scale applications. Fortunately for us, we have a magic trick up our sleeves... drumroll 🥁 ...Dependency Injection!!! 🙌

We could avoid all of that unnecessary modification by just requiring the engine to be passed as a dependency. This allows us to easily modify the engine type on our class object (talk about an upgrade 😉). Enough talk, here's how we do that.


class Car(private val engine: Engine) {  // Engine is injected via the constructor
    fun drive() {
        engine.start()
        println("Car is driving")
    }
}

fun main() {
    val engine = Engine()  // engine dependency is created outside the Car class
    val car = Car(engine)  // dependency is injected into the Car class
    car.drive()
}

We could take this a step further by defining an Engine interface. This provides more flexibility, enabling us to swap out different implementations of the dependency without changing the dependent class.

First, we define our interface and also two contracts (start & stop) that all engines must follow.

interface Engine {
    fun start()
    fun stop()
}

now let's create two implementations of the Engine interface.

class PetrolEngine() : Engine {
    override fun start() {
        println("engine started")
    }

    override fun stop() {
        println("engine stopped")
    }
}

class ElectricEngine() : Engine {
    override fun start() {
        println("Electric engine started, battery on 12% please find a charging station soon")
    }

    override fun stop() {
        println("electric engine stopped")
    }
}

now our Car class would depend on the Engine interface and not a specific implementation.

class Car(private val engine: Engine) {
    fun drive() {
        engine.start()
        println("Car is moving")
    }

    fun stop() {
        println("Car stopped turning off engine")
        engine.stop()
    }
}

finally here's how we can use both implementations.

    val petrolEngine: Engine = PetrolEngine()
    val electricEngine: Engine = ElectricEngine()

    val carWithPetrolEngine = Car(petrolEngine)
    carWithGasolineEngine.drive()

    val carWithElectricEngine = Car(electricEngine)
    carWithElectricEngine.drive()

This approach using an interface is more scalable and is a very common practice in software design, especially when building apps that need to be easily modified.

I guess now we could easily solve the world's energy crisis by implementing an Engine that runs on water 😅.


This content originally appeared on DEV Community and was authored by David Rufai


Print Share Comment Cite Upload Translate Updates
APA

David Rufai | Sciencx (2024-08-18T18:07:08+00:00) A Guide to Dependency Injection. Retrieved from https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/

MLA
" » A Guide to Dependency Injection." David Rufai | Sciencx - Sunday August 18, 2024, https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/
HARVARD
David Rufai | Sciencx Sunday August 18, 2024 » A Guide to Dependency Injection., viewed ,<https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/>
VANCOUVER
David Rufai | Sciencx - » A Guide to Dependency Injection. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/
CHICAGO
" » A Guide to Dependency Injection." David Rufai | Sciencx - Accessed . https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/
IEEE
" » A Guide to Dependency Injection." David Rufai | Sciencx [Online]. Available: https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/. [Accessed: ]
rf:citation
» A Guide to Dependency Injection | David Rufai | Sciencx | https://www.scien.cx/2024/08/18/a-guide-to-dependency-injection/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.