Basics of Dependency Injection

24 July 2020 at 12:30 by ParTech Media - Post a comment

What is Dependency Injection?

Dependency Injection (DI) continues to grow in popularity due to its code simplification effects. This article presents a high level overview of Dependency Injection (DI). It aims to present a quick intro to Dependency Injection: what it is, and when to use it.

DI is the design pattern that can help developers decouple the different pieces of their applications. To put it simply, when class A uses some functionality of class B, then it’s said that class A has a dependency of class B. For example, in Java, before we can use methods of other classes, we first need to create the object of that class (i.e. class A needs to create an instance of class B).

We can explain it as a technique where an object supplies the dependencies to another object. This pattern allows someone to remove hard-code dependencies and make it possible to change them. Dependencies can be injected to the object via the constructor; via the defined methods or a setter property.

ASP.NET Core provides you with extensive support to Dependency Injection. ASP.NET Core injects objects of dependency classes through constructor or method by using built-in Inversion of Control (IoC) container.

Understanding Dependency Injection and its importance

To simply explain, a prominent Software Development Q&A website called Stack Overflow, features an answer to the question, “How to explain Dependency Injection to a 5-year old?” The most highly rated answer, by John Munsch, provides a precise similarity.

How to explain Dependency Injection to a 5-year old?” by John Munsch https://stackoverflow.com/questions/1638919/.

It explains to a 5-year old, when you go and get things out of the refrigerator for yourself, you can cause problems. You may leave the door open; you may get something which Mom or Dad doesn’t want you to have. You may even be looking for something we don’t even have or which has expired.

Actually, you should be stating a need, “I need something to drink with lunch,” and then Mom and Dad will ensure that you have something to drink when you sit down to eat lunch.

In terms of object-oriented software development, it means - Collaborating classes (the 5-year old) should rely on infrastructure (the parents) to provide required services. Any application is composed with many classes that collaborate with each-other to accomplish some useful stuff. Conventionally, each object is responsible for obtaining its own references to the dependent objects (i.e. dependencies) it collaborates with.

Consider a Car object. A Car depends on Wheels, Engine, Fuel, Battery etc. to run. Traditionally we define the brand of such dependent objects along with the definition of Car object.

class Car
{
    private Wheel wh = new MRFRubberWheel();
    private Battery bt = new ExcideBattery();
	//...
}

In this example, the Car object is responsible for creating the dependent objects. If we want to change the type of its dependent object - say Wheel after the previous MRFRubberWheel() punctures. We need to redefine the Car object with its new dependency say ApolloRubberWheel(), but only the Car manufacturer can do that.

Then, what the Dependency Injection stands for? When using Dependency Injection, objects are given their dependencies on run time rather than compile time (car manufacturing time). In other words, objects are configured by an external entity (by us not by manufacturer). So, we can now change the Wheel whenever we want. Here, the Dependency (Wheel) is injected to Car at run time. It is called Dependency Injection.

Why should you use Dependency Injection?

DI aids loose coupling, and loose coupling makes code more maintainable. The foremost idea of using DI is to decouple your classes constructor from the construction of its dependencies, so that you will not have to instantiate anything in the dependent class.

For example, let’s say we have a Car class that contains many objects like wheels, engine, etc. The car class is responsible for creating all the dependency objects. Suppose, if we decide to discontinue MRF Wheels in the future and want to use Bridgestone Wheels? We will need to recreate the car object with a new dependency. But when using dependency injection (DI), we can change the Wheels at runtime as dependencies can be injected at runtime rather than at compile time.

You can think of DI as the middleman in our code who does all the work of creating the preferred wheels object and providing it to the Car class. It makes our Car class independent from creating the objects of Wheels, Battery, etc.

Benefits of using DI

Some vital benefits of using DI are listed below:

  1. It helps in unit testing. Classes can be unit tested.

  2. Boiler plate code is reduced; as initializing of dependencies is done by the injector component.

  3. Code can be extended and reused in different ways not obviously planned for. Extending the application becomes easier. Very useful for large projects where there is an issue of maintainability, simplicity and several others.

  4. Helps to enable loose coupling, which is imperative in application programming. Classes with clearly defined responsibilities are easier to maintain. Key benefit is loose coupling between dependent objects, if an object operates on their dependencies by their interface not by implementation then compile time dependency can be swapped out with Dependency Injection.

  5. Helps in Late Binding. Services can be swapped with other services without recompiling code.

Advantages of DI

Dependency injection is useful when working with large applications because it releases various code modules from the task of instantiating references to resources and allows dependencies and even mock dependencies to be swapped out easily, that makes unit testing easier. By letting the framework to do the resource creation, configuration data is centralized and updates only occur in one place.

And, another benefit of dependency injection is that injected resources can be customized through XML files that are outside the source code. This lets changes to be applied without having to recompile the entire codebase.

Disadvantages of DI

Disadvantages of DI:

  1. It is slight difficult to learn, and if overused can lead to management issues and other difficulties.

  2. Several compile time errors are pushed to run-time.

  3. Dependency injection frameworks are implemented with dynamic programming. This can hamper use of IDE automation, like *find references*, *show call hierarchy* and *safe refactoring*.

And, dependency injection can occasionally make troubleshooting tough, as a great deal of code is pushed into an unknown location that creates resources and distributes them as required across the application. Debugging code when all of the objects that are unruly are concealed in a complicated third-party framework can be very annoying, overwhelming and time-consuming.

Types of Dependency Injection

The injector class injects dependencies basically in three ways - through a constructor, through a property, or through a method. There are three types of dependency injection as listed below:

  1. Constructor Injection: In the Constructor Injection, dependencies are provided through a class constructor.

  2. Setter Injection: In the Setter Injection, the injector supplies the dependency through a public property of the client class.

  3. Interface Injection: In this type of injection, the client class implements an interface which declares the method(s) to supply the dependency and the injector uses this interface to supply the dependency to the client class. Clients must implement an interface that exposes a setter method that accepts the dependency.

It is the Dependency Injector’s responsibility to:

  • Create the objects
  • Know which classes need those objects
  • Then, make available to them all those objects

If there is any change in objects, then the DI looks into it and it doesn’t concern the class using those objects. So, if the objects change in the future, then its DI’s responsibility to make available the appropriate objects to the class.

Libraries and Frameworks

Some useful libraries and frameworks that implement DI are listed below:

  • Spring Framework: A substantially large framework which offers a number of other capabilities apart from Dependency Injection.

  • PicoContainer: A fairly small tightly focused DI container framework.

  • HiveMind: Another DI container framework.

  • XWork : Primarily a command pattern framework which very effectively leverages Dependency Injection. While it is an independent framework in its own right, it is often used in conjunction with Webwork

  • Spring (Java)

  • Google Guice (Java)

  • Dagger (Java and Android)

  • Autofac (.NET)

  • Castle Windsor (.NET)

  • Unity(.NET)

Conclusion

When you use Dependency Injection, there are a number of styles to choose from. It is widely suggested that you follow constructor injection unless you run into specific problems with this approach. If you are selecting to build or obtain a container, look for one that supports both Constructor and Setter injection.

Latest