HI WELCOME TO SIRIS

Dependency Injection

Leave a Comment
In the previous chapter of DIP, we created and used abstraction to make the classes loosely coupled. Here, we are going to implement Dependency Injection and strategy pattern together to move the dependency object creation completely out of the class. This is our third step in making the classes completely loose coupled.

Dependency Injection (DI) is a design pattern used to implement IoC where it allows creation of dependent objects outside of a class and provides those objects to a class through different ways. Using DI, we move the creation and binding of the dependent objects outside of the class that depends on it.
Dependency Injection pattern involves 3 types of classes.
  1. Client Class: The client class (dependent class) is a class which depends on the service class
  2. Service Class: The service class (dependency) is a class that provides service to the client class.
  3. Injector Class: The injector class injects service class object into the client class.
The following figure illustrates the relationship between these classes:

Dependency Injection
As you can see, injector class creates an object of service class, and injects that object to a client object. This way DI pattern separates the responsibility of creating an object of service class out of client class.

Types of Dependency Injection

As you have learned above, the injector class injects the service (dependency) to the client (dependent). The injector class injects dependencies broadly in three ways: through constructor, through property, or through method.
Constructor Injection: In the constructor injection, injector supplies service (dependency) through the client class constructor.
Property Injection: In property injection (aka Setter Injection), injector supplies dependency through a public property of the client class.
Method Injection: In this type of injection, client class implements an interface which declares method(s) to supply dependency and the injector uses this interface to supply dependency to the client class.
Let's take an example from the previous chapter to maintain the continuity. In the previous section of DIP, we used Factory class inside CustomerBusinessLogic class to get an object of CustomerDataAccess object as shown below.
public interface ICustomerDataAccess
{
    string GetCustomerName(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess() {
    }

    public string GetCustomerName(int id) {
        return "Dummy Customer Name";        
    }
}

public class DataAccessFactory
{
    public static ICustomerDataAccess GetCustomerDataAccessObj() 
    {
        return new CustomerDataAccess();
    }
}

public class CustomerBusinessLogic
{
    ICustomerDataAccess _custDataAccess;

    public CustomerBusinessLogic()
    {
        _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj();
    }

    public string GetCustomerName(int id)
    {
        return _custDataAccess.GetCustomerName(id);
    }
}
The problem with the above example is that we used DataAccessFactory inside CustomerBusinessLogic class. So, suppose there is another implementation of ICustomerDataAccessfor some reason and we want to use that new class inside CustomerBusinessLogic. Then, we need to change the source code of CustomerBusinessLogic class also. Dependency injection pattern solves this problem by injecting dependent objects via constructor, property, or interface.
The following figure illustrates the DI pattern implementation for the above example.
Dependency Injection
As you see, CustomerService class becomes injector class which sets an object of service class (CustomerDataAccess) to the client class (CustomerBusinessLogic) either through constructor, property, or method to achieve loose coupling. Let's explore each of these options.

Constructor Injection

As mentioned before, when we provide dependency through the constructor then it's constructor injection.
Consider the following example where we have implemented DI using constructor.
Example: Constructor Injection
public class CustomerBusinessLogic
{
    ICustomerDataAccess _dataAccess;

    public CustomerBusinessLogic(ICustomerDataAccess custDataAccess)
    {
        _dataAccess = custDataAccess;
    }

    public CustomerBusinessLogic()
    {
        _dataAccess = new CustomerDataAccess();
    }

    public string ProcessCustomerData(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
}

public interface ICustomerDataAccess
{
    string GetCustomerData(int id);
}

public class CustomerDataAccess: ICustomerDataAccess
{
    public CustomerDataAccess()
    {
    }

    public string GetCustomerName(int id) 
    {
        //get the customer name from the db in real application        
        return "Dummy Customer Name"; 
    }
}
In the above example, CustomerBusinessLogic includes constructor with one parameter of type ICustomerDataAccess. Now, the calling class must inject an object of ICustomerDataAccess.
Example: Inject Dependency
public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic(new CustomerDataAccess());
    }

    public string GetCustomerName(int id) {
        return _customerBL.GetCustomerName(id);
    }
}
As you can see in the above example, CustomerService class creates and injects CustomerDataAccess object into CustomerBusinessLogic class. Thus, CustomerBusinessLogic class need not create an object of CustomerDataAccess using new keyword or using factory class. The calling class (CustomerService) creates and sets appropriate DataAccess class to the CustomerBusinessLogic class. This way CustomerBusinessLogic and CustomerDataAccess class become more loosely coupled classes.

Property Injection

In the property injection, dependency is provided through public property. Consider the following example.
Example: Property Injection
public class CustomerBusinessLogic
{
    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        return DataAccess.GetCustomerName(id);
    }

    public ICustomerDataAccess DataAccess { get; set; }
}

public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic();
        _customerBL.DataAccess = new CustomerDataAccess();
    }

    public string GetCustomerName(int id) {
        return _customerBL.GetCustomerName(id);
    }
}
As you can see above, the CustomerBusinessLogic class includes public property named DataAccesswhere you set an instance of a class that has implanted ICustomerDataAccess. So, CustomerServiceclass creates and sets CustomerDataAccess class using this public property.

Method Injection

In the method injection, dependencies are provided through methods. This method can be a class method or interface method.
The following example demonstrates method injection using interface based method.
Example: Interface Injection
interface IDataAccessDependency
{
    void SetDependency(ICustomerDataAccess customerDataAccess);
}

public class CustomerBusinessLogic : IDataAccessDependency
{
    ICustomerDataAccess _dataAccess;

    public CustomerBusinessLogic()
    {
    }

    public string GetCustomerName(int id)
    {
        return _dataAccess.GetCustomerName(id);
    }
        
    public void SetDependency(ICustomerDataAccess customerDataAccess)
    {
        _dataAccess = customerDataAccess;
    }
}

public class CustomerService
{
    CustomerBusinessLogic _customerBL;

    public CustomerService()
    {
        _customerBL = new CustomerBusinessLogic();
        ((IDataAccessDependency)_customerBL).SetDependency(new CustomerDataAccess());
    }

    public string GetCustomerName(int id) {
        return _customerBL.GetCustomerName(id);
    }
}
In the above example, CustomerBusinessLogic class implements IDataAccessDependency interface which includes method SetDependency. So the injector class (CustomerService) will now use this method to inject dependent class (CustomerDataAccess) to the client class.
Thus, you can use DI and strategy pattern to create loose coupled classes.
So far, we have used couple of principles and patterns to achieve loosely coupled classes. In professional projects, there would be many dependent classes and implementing these patterns would be time consuming. Here IoC Container (aka DI container) helps us. Learn about IoC Container in the next chapter.

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.