Dependency Injection Design Pattern and IoC Principle

2022-04-04

 Consider a typical scenario where we have a client class which calls another class (can be referred here as service class as it provides service to the calling client class). So the client class has dependency with a specific service class. Now later if there is a new service to be invoked by the client then we need to rewrite the client class in the area where the existing service class to be replaced with new service class. This is called dependency of the client with its service class and its clear example of tight coupling. Dependency injection is a design pattern which eliminates this type of dependency of a class independent of its dependencies. 

This DI is an example of one way of achieving the Dependency Inversion principle found in the SOLID philosophy. The practice of dependency injection is made possible by following the dependency inversion principle.

Typical code without DI pattern will look like as given below:


using System;
namespace DISample
{
    class Program
        {
            static void Main(string[] args)
                {
                    Customer objClient=new Customer();
                    string orderResponse = objClient.OrderFood();
                    Console.WriteLine(orderResponse);
                }
         }

Public class Customer
{
    public string OrderFood()
        {
            Zomato objShopAService=new Zomato();
            return objShopAService.Order();    
        }
}

Public class Zomato
{
    public string Order()
    {
        return "Thanks for ordering in Zomato. Please wait we are going to deliver asap.";
    }
}

public class Swiggy
{
    public string Order()
    {
      return "Thanks for ordering in Swiggy. Your food will be delivered soon by our courier guy.";
    }
}

Public class UberEats
{
   public string Order()
   {
     return "Thanks for ordering in UberEats. We are sending your food now!";
   }
}

}

So what if the client class here (i.e. Customer changes his mind to place the order from UberEats instead of Zomato. Since the customer class is currently tightly coupled as per the implementation we need code to be recompiled whenever the client wishes to change its services. And this implementation is difficult to unit test.

Now let us use the how DI design pattern helps to decouple the classes and how the class can be independent from the creation of the objects it depends on. 

There are 4 components in the DI design pattern which helps in achieving this pattern:

1. Client class; client class depends on the service class(in sample its Customer class)

2. Service class; which provides service to the client class(in sample its Swiggy,Zomato,UberEats class)

3. Interface: (the interface which defines the contract of the service class, interface is IOnlineFoodDelivery in below sample. As the Dependency Inversion Principle suggests modules should depend on abstractions. So to have the abstractions implemented we go for an abstraction with the help of interface)

4. Injector: (Injector class injects the service class object into the client class, injector is done in Main() of Program class in below sample)


using System;
namespace DISample
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer objCust=new Customer(new Zomato());
        }
}

public class Customer
{
    IOnlineFoodDelivery _iOnlineFoodDelService;
    public Customer(IOnlineFoodDelivery pIOnlineFoodDelivery)
    {

        _iOnlineFoodDelService=pIOnlineFoodDelivery;
        Console.WriteLine(_iOnlineFoodDelService.Order());
    }
}

public interface IOnlineFoodDelivery
{
    public string Order();
}

public class Zomato:IOnlineFoodDelivery
{
    public string Order()
       {
            return "Thanks for ordering in Zomato. Please wait we are going to deliver asap.";
       }
}

public class Swiggy:IOnlineFoodDelivery
{
    public string Order()
    {
        return "Thanks for ordering in Swiggy. Your food will be delivered soon by our courier guy.";
    }
}

public class UberEats:IOnlineFoodDelivery
{
    public string Order()
    {
        return "Thanks for ordering in UberEats. We are sending your food now!";
    }
 }
}

Types of Dependency Injection:
There are 3 ways how the injector can inject the service class object to the client
1. Constructor injection: The dependencies are provided through a client's class constructor.(above sample code)
2. Setter injection: The client exposes a setter method that the injector uses to inject the dependency.
3. Interface injection: The dependency's interface provides an injector method that will inject the dependency into any client passed to it.

Dependency injection separates the creation of a client's dependencies from the client's behavior, which promotes loosely coupled programs and the dependency inversion and single responsibility principles.

Dependency injection is an example of design pattern which implements the Inversion of Control(IoC). IoC is a programming principle, IoC inverts the flow of control as compared to traditional control flow.

Inversion of control serves the following design purposes:
To decouple the execution of a task from implementation.
To focus a module on the task it is designed for.
To free modules from assumptions about how other systems do what they do and instead rely on contracts.
To prevent side effects when replacing a module.

Inversion of control is sometimes facetiously referred to as the "Hollywood Principle: Don't call us, we'll call you".

As a discipline, dependency injection separates how objects are constructed from how they are used. As a result, dependency injection frameworks often diminish the importance of the new keyword found in most object-oriented languages.

0 comments: