DEV Community

Cover image for A beginner's guide to Dependency Injection
Tobi Alagbe
Tobi Alagbe

Posted on

A beginner's guide to Dependency Injection

Dependency injection is one of those topics new developers have a not so easy time grasping. This is because a lot of materials online make it seem a lot more complicated than it is. Is dependency injection a tool, framework or library? Well, none of the above. Dependency Injection is a software design pattern.

A software design pattern is a guideline that relates the description of a problem and a solution to that problem, so that developers can use this pattern to simplify the solution of common problems and cases in software development. According to wikipedia it is “a general, reusable solution to a commonly occurring problem within a given context in software design”.

So you know how I mentioned earlier that dependency injection is a software design pattern, well, that’s not entirely true. It is actually a subset of a design pattern called inversion of control (IoC), but for the purpose of this article we would stay within the bounds of dependency injection. Now without further ado, what the heck is dependency injection?

A Formula One driver would usually make about one to four pit stops in a race depending on the circuit, this allows the pit crew to quickly change the race car tyres before the race continues. One way for the driver to change tyres would be to carry a spare in the car and the stop midway while he/she changes the tires, this would obviously be very slow and inefficient. By not bothering about the dependency (fresh tyres) the driver is able to delegate the injection of the dependency to the pit crew. This is dependency injection. Coming back to software development lingo, a dependency is just another object that your class needs to function. Injecting dependencies just means that the dependency is pushed into the class from the outside, meaning you shouldn’t instantiate new class dependencies using the new operator from within the class, instead take it as a constructor parameter or via a setter. That’s all there is to it.

To illustrate this, let’s build a case application;

public class FormulaOneCar{

    private RoundWheels tyres;

    public FormulaOneCar(){
       this.tyres = new RoundWheels();
    }

    public void Race(){
        tyres.run();
    }

}

Enter fullscreen mode Exit fullscreen mode

The above code snippet illustrates the instinctive way a developer might attempt to instantiate a class dependency, but we already know that the dependency injection pattern demands the inverse of this by delegating the instantiation to an external party. So let’s look at how to achieve this following the dependency injection pattern.

public class FormulaOneCar{

    private RoundWheels tyres;

    public FormulaOneCar(RoundWheels wheels){
      this.tyres = new wheels();
    }

    public void Race(){
       tyres.run();
     }

}

Enter fullscreen mode Exit fullscreen mode

In the above example we are able to relinquish control of instantiating the dependency to an external, the dependency is injected through the class constructor eliminating any need for using the new keyword within the class. This allows for a less tightly coupled system which is one of the characteristics of good software design. But…

You might be asking, what is this external party that is so kind as to help me with this task of handling and injecting my dependencies and how does it know what I need? The answer is, the Dependency Injection Container.

Dependency Injection Container

A Dependency Injection Container is an object that knows how to instantiate and configure objects. It is often configured during registration with logic like, if a class asks for a dependency of this type, create an object of this type and inject it into it’s constructor. The container would usually provide handling methods for the dependency injection lifecycle

Registration: The container would know the dependency to instantiate when presented with a particular type. This process involves a type mapping.

Resolving: We don't need to create objects manually. The container does it for us. This method resolves the type indicated by creating an object of the type and injecting the dependencies before returning the object.

Disposal: Most containers would provide ways to manage an object's lifecycle and dispose of it when necessary.

This has been an introduction to dependency injection without going too deep. In the following article we would be taking a look at the Angular frontend framework and how dependency injection is at its core.

Top comments (0)