Dependency Injection simplified

Ridwan Yusuf
2 min readJan 8, 2023

--

Don't freak out on this topic because it's not as difficult as the internet makes it out to be.

My approach to explaining technical concept focuses first on the surface/literal meaning of the topic in question.

Here we go **dependency injection(DI)**

Dependency when you think of it could probably mean a package/library/object/function or just something else needed/required to accomplished a task.
Injection, simply put, refers to its usage or the process of inserting the dependency where it is used.
A conclusive definition therefore is the process of injecting/inserting a needed dependency into where it is used.

Technically 👨‍💻 , it is a technique in which an object receives other objects it depends on.

Take a look at this simple class definition below in Python where we are not using Dependency Injection(DI):

[*] class definitions

class Engine:
def start(self):
return "Engine starts"
class Car:
def start(self):
return Engine().start()

[*]Code Usage:

car = Car() # instantiate a car object
print(car.start()) # calling start() method on the car instance

[*] Expected output
Engine starts

If you have been programming like this please stop.
This code works but programming with this approach is wrong for some reasons:
-The Car class is tightly coupled(attached) to the Engine even though the Engine is a dependency. As you can see we are creating the Engine inside of the Car itself.

For any changes we make to Engine, we must always look for all the Cars where it is used.
Say we rename start() in the Engine to move(), we must look for every single Car for a rename.

-This kind of code is hard to unit test. If we need to test the Car, we cannot easily mock the dependency(Engine).

So Dependency Injection is the solution here. We inject the dependency (Engine in this case) to another class/function (Car) which needs it.
[*]

class Engine:
def start(self):
return "Engine starts"
class Car:
def __init__(self, engine): # constructor method
self.engine = engine

def start(self):
self.engine.start()

[*]usage
engine = Engine() # a dependency
car = Car(engine) # dependency injected/inserted to where it's used.

With this approach,
-Unit Testing this code become easier since we'd just need to pass a mocked version of engine into the Car.

-Code reusablity. I can reuse the Car in as much the object passed into it implement the start() behaviour. Infact we can pass any object into the Car in as much it implement the start() method.

The above method used constructor method.
Here’s another approach using setter method.class Engine:
def start(self):
print('Engine starts')

class Car:
def set_engine(self, engine):
self.engine = engine

engine = Engine()
car = Car()
car.set_engine(engine)
car.engine.start()

Hopefully this technique makes you write clean code going forward.

Thanks for reading and feel free to explore my video collection on YouTube for more educational content. Don’t forget to subscribe to the channel to stay updated with future releases.

--

--

Ridwan Yusuf
Ridwan Yusuf

Written by Ridwan Yusuf

RidwanRay.com -I provide engineers with actionable tips and guides for immediate use

No responses yet