What is Dependency Injection?
To answer that question we must first answer another question: “What is a dependency?”. A dependency is a file your code needs to run/compile properly. Dependencies are usually visible to most devs in the form of “import” statements at the top of their classes/scripts/code. Dependencies are usually thought of as something external to your code that you bring in to make things easier. In the context of dependency injection think of a dependency as another class that’s local to your project but outside of the class you’re working on. Lets call it “extClass”. The class you’re working on will need this class to perform some action. Lets call it “currClass”. Without extClass, currClass doesn’t work correctly. This means that currClass is dependent on extClass.
If that seemed obvious to you then you’ve likely written code in an object oriented language before. Which means you’re probably thinking “Yeah, using classes you’ve previously written in classes you’re currently writting is pretty much how this whole programming thing works, right? It’s all ‘dependent’, so what’s the problem?”. Well you’re right… mostly. I’ll explain.
When would you use it and why is it valuable?
Objects can be dependent on one another without issue. The concept of Dependency Injection comes into play when your code needs to perform an action such as saving to a database or making a call to an REST API or something. In object oriented languages (and possibly others, idk) the thing your code needs to perform an action will appear as an object in your code.
To illustrate this idea, lets say you’re working on the frontend of an application and you’ve got an object your code needs to interact with your backend API called “serviceObj”. If something goes wrong with your serviceObj you handle the error and everything is still cool, no problem but what happens when you want to make a change to your backend? You now have to update “serviceObj” everywhere that you’ve used it. Essentially your frontend is tied to your backend and because of that little changes in the backend could mean big changes everywhere in your frontend (there are other examples of when DI is useful but this post is targeting a basic explanation). Wouldn’t it be great if your frontend wasn’t dependent on serviceObj?
There’s multiple ways to fix this with dependency injection. One way to do it would be to define an interface for your backend service and use it everwhere in your frontend. You’d then define a “factory” class that contains an actual implementation of your serviceObj with some logic to decide when to return the serviceObj. Now your frontend isn’t dependent on serviceObj. When there’s a change to the backend, the only updates that might need to be made will be in the factory class (and possibly it’s interface). Now your frontend will get what it needs when it needs it and you’ll never have to update it based on backend changes (in a perfect world). This is called “Interface Injection”.
Another way of adding dependency injection to your code would be a framework. Since I’ve most recently been working in Java, I’m familiar with Spring’s Dependency Injection (DI). To use Spring’s DI, you’d basically create an interface for your backend and then, instead of creating a factory class, add an annotation above each place you’d like Spring to inject a dependency. At run-time Spring will put everything where it needs to go. This is also Interface Injection but with some Spring magic thrown in.
Dependency Injection is a design pattern that grew out of a programming principle called “Inversion Of Control”.
What is Inversion of control?
To state it simply, Inversion of Control is when the flow of execution of your program isn’t determined until run-time. i.e. you don’t know what will happen until you run it. In our example, your frontend won’t know what object it will be operating on until your code is running. In IoC your code stops caring about how the thing is done and focuses on the fact that it gets done by calling the service/object that can get it done.