DEV Community

Explain Dependency Injection (DI) like I am five.

Mirza on June 12, 2020

I have watched some videos about it but they weren't really helpful in explaining the underline workings and philosophy. All I know is that some frameworks like .NET Core and Spring use it extensively.

Collapse
 
gjrdiesel profile image
Justin

Say you’re hungry. With dependency injection, you can just tell mom you’re hungry and she’ll feed you something that matches the food interface.

Without DI, you decide and gather the bread, ketchup, mustard and a hotdog put it all in a bin and bring it to mom and she makes you a hot dog.

Collapse
 
tam360 profile image
Mirza

So it's kind of like high level abstraction which hides all the complexities and give you what you really need. Interesting.

Collapse
 
chhuang215 profile image
Chih-Hsuan Huang

Isn't this what IOC is?

Collapse
 
tam360 profile image
Mirza

I think DI uses the idea of IoC behind the scenes

Collapse
 
ermiyaeskandary profile image
Ermiya Eskandary

Hey Mirza, literally have been in the same boat as you
Hope this helps!

In a nutshell, what it means is that any given piece of code you write should be provided everything it needs to do its job, instead of creating things for itself. So say I'm writing a forum, and I'm specifically writing the code that adds a new user record to the database and sends them an email to confirm their registration. If I used dependency injection, that piece of code would be given a data model/database connection to work with (instead of creating a new instance of one) and a external class with a pre-defined interface to send email instead of creating an SMTP client object. The reason you do this is it makes the code much easier to test in isolation. So I can replace the class instance for sending emails with one that just checks one is sent to a know address to help with unit tests. It also means that components are easier to replace later on because you can just swap out what objects you give it with objects of a different class with a matching interface.

Let me know if you need any more help :)

Collapse
 
nestedsoftware profile image
Nested Software • Edited

A simple concrete example (using a vaguely Java/C# pseudocode): You want to save some data to a database. You can do something like this:

public class PetManager {
  public PetManager() {
    _dbConnection = new FancyDbConnection("some_connection_string");
  }
  public void savePet(pet) {
    _dbConnection.save(pet);
  }
}
Enter fullscreen mode Exit fullscreen mode

Your PetManager class is responsible for creating and configuring the database, then uses that connection to save a pet record. With dependency injection, the key idea is that the dependency, which is the database connection in this case, can be passed in to the PetManager class from outside (usually as an interface). So it would look like this:

public class PetManager {
  public PetManager(DbConnection dbConnection) {
    _dbConnection = dbConnection;
  }
  public void savePet(pet) {
    _dbConnection.save(pet);
  }
}
Enter fullscreen mode Exit fullscreen mode

This makes it easier to unit test your PetManager class, since you can pass a test-only version of DbConnection. Also, if your application is running in a container of some kind, you can configure the database connection properties in the container, and then let the container wire up the database connection for you at run time. This gives you additional flexibility for setting up these kinds of external dependencies, which can also help with scalability and performance.

Collapse
 
brandinchiu profile image
Brandin Chiu

Dependency injection is used to allow your application to share instances of specific classes instead of you having to instantiate and initialize them many times around your application.

How this happens will vary with each framework, but the goal is to relegate the management of your classes (typically things like service classes) to the framework instead of your application.

The framework will instantiate the class in question at runtime, and then your code will ask for it using an identifier somewhere instead of creating a new one. This way, everywhere in your code is always using the same instance of the class.

Tl;dr: dependency injection makes the framework instantiate classes for your application and shares them with other parts of your code instead of you needing to call new Foo() yourself. It helps reduce memory usage.

Collapse
 
tam360 profile image
Mirza

Are there any drawbacks of challenges associated with DI?

Collapse
 
brandinchiu profile image
Brandin Chiu

Depending on your school of thought, some people may not find the way you interact with your classes to be terribly intuitive, as you need to have a good understanding of the underlying management.

Also, depending on which framework you're using, it may be easier or more difficult to handle how the framework manages constructor arguments for the classes you need.

On the bright side though it makes unit tests simpler because it's very easy to stib/mock your services since you're never interacting with them directly.

Thread Thread
 
tam360 profile image
Mirza

I guess the main challenge of using DI is having a solid knowledge of interface/class's internal workings. Since DI does all the things behind the curtain, it becomes annoying when one starts using them without understanding. This I think is true in the case of debugging.

Thread Thread
 
brandinchiu profile image
Brandin Chiu

For sure.

I personally think the pros outweigh the cons, but to each their own.

Collapse
 
derekjhopper profile image
Derek Hopper

I find code examples best for illustrating, so I'll share an example and try to explain. This is valid Ruby code, but will hopefully read more like pseudo code.

Without dependency injection:

class Teacher
  def grade(title, content)
    Paper.new(title, content).grade
  end
end
  • Anytime you want to grade a new type of paper, you need to update Teacher.
  • If the arguments of Paper change, you need to update Teacher.
  • Basically, the Teacher class has many reasons to change.

With dependency injection:

class Teacher
  def grade(paper)
    paper.grade
  end
end

teacher.grade(Paper.new(title, content))
teacher.grade(BookReport.new(book, title, content))
teacher.grade(ComputerScienceEssay.new(content))
  • By default, you can grade many types of papers. As long as any new Paper has a grade method, the Teacher can handle it.
  • The Teacher class has fewer reasons to change now.

I realize this isn't a simple explanation yet, so I'll try to simplify. Dependency injection gives you a Teacher that knows how to grade any paper you assigned to students in the past and any paper you might assign in the future. Without it, the Teacher needs to go back to school whenever a new paper is assigned.

Collapse
 
mxldevs profile image
MxL Devs

Dependency Injection is basically me giving you a button for you to press.

class Person {
   function pressButton(Button theButton) {
       theButton.press();
   }
}

You don't need to know what the button does. You just need to know that the button is to be pressed.

The button you get might do cool things, and you don't even need to worry about how the button does it.

But you might also be kind of concerned because you don't know what the button does beforehand which makes it somewhat RISKY to press.