Dependency Injection In Software Engineering

Dependency Injection

Asking for your dependencies instead of constructing them within the application logic is called "Dependency Injection". This is a software design pattern that implements Inversion Of Control. It shows how to create loosely-coupled classes. Loosely-coupled means the object should only have as many dependencies as it needs to do its jobs, so that the dependencies should be few. Objects should be dependent on an interface that promotes greater reusability. Dependency Injection allows you to inject an object into the class. So there is no need to create concrete objects (using the new keyword) in the class. Objects will be injected into a class by constructors or setter methods, rather than relying on the class to create the object itself.

When to use Dependency Injection

  • Dependency Injection is very useful when we implement loosely-coupled modules.
  • Unit testing is one of the most useful advantages of Dependency Injection.
Before we start the implementation of loosely-coupled classes using Dependency Injection, we must start with a small idea about “loosely-coupled” and “tightly-coupled” classes. And Why Dependency Injection is preferred for loosely-coupled class implementation.

Tightly-Coupled

When a class is dependent on a concrete dependency, it is said to be tightly-coupled to that class. For an enterprise-level application it is really difficult to make a change if it is tightly-coupled. Because a change in objects may require changes to a number of other objects.


  1. public class CustomerBL  
  2. {  
  3.     public string GetDALString()  
  4.     {  
  5.         //Calling CustomerDAL  
  6.         CustomerDAL customerObject = new CustomerDAL();  
  7.         return customerObject.GetString();  
  8.     }  
  9. }  
  10.   
  11. public class CustomerDAL  
  12. {  
  13.     public string GetString()  
  14.     {  
  15.         return "Hello World";  
  16.     }  
  17. }  
The classes are tightly-coupled. Now take a situation, say for some specific reason you need a CustomerDAL or CustomerDAL1 class depending on a different situation.
  1. public class CustomerDAL1  
  2. {  
  3.     public string GetString()  
  4.     {  
  5.         return "Hello World…1";  
  6.     }  
  7. }  
This is the main problem of tightly-coupled classes.

loosely-coupled

Objects are not concretely dependent. It reduces the inter-dependencies among components of a system that the risk that changes in one component will require changes in any other component.


  1. public interface ICustomerDAL    
  2. {    
  3.     string GetString();    
  4. }    
  5.   
  6. public class CustomerDAL : ICustomerDAL    
  7. {    
  8.     public string GetString()    
  9.     {    
  10.         return "Hello World";    
  11.     }    
  12. }    
  13.   
  14. public class CustomerDAL1 : ICustomerDAL    
  15. {    
  16.     public string GetString()    
  17.     {    
  18.         return "Hello World...1";    
  19.     }    
  20. }    
  21.   
  22. public class CustomerBL    
  23. {    
  24.     private ICustomerDAL _customerObject;    
  25.   
  26.     public CustomerBL()    
  27.     {    
  28.         _customerObject = new CustomerDAL();    
  29.     }    
  30.     public CustomerBL(bool condition)    
  31.     {    
  32.         if (condition)    
  33.             _customerObject = new CustomerDAL();    
  34.         else    
  35.             _customerObject = new CustomerDAL1();    
  36.   
  37.     }    
  38.     public string GetDALString()    
  39.     {    
  40.         return _customerObject.GetString();    
  41.     }    
  42. }     
So we have introduced an interface to make those classes loosely-coupled.

But the problem is we are initiating an object in the class. That means any changes will force us to change all the referenced classes from where we are calling the CustomerDAL class.

To overcome this problem we introduce a new design pattern called Factory Pattern.

Factory Pattern: Let's start with a small idea of Factory Pattern.



Now see the code sample of loosely-coupled classes using a Factory Pattern.
  1. public interface ICustomerDAL    
  2. {    
  3.     string GetString();    
  4. }    
  5.   
  6. public class Factory    
  7. {    
  8.     public static ICustomerDAL InstantiateCustomer()    
  9.     {    
  10.         if (true//Condition here    
  11.             return new CustomerDAL();    
  12.         else    
  13.             return new CustomerDAL1();    
  14.     }    
  15. }    
  16.   
  17. public class CustomerDAL : ICustomerDAL    
  18. {    
  19.     public string GetString()    
  20.     {    
  21.         return "Hello World";    
  22.     }    
  23. }    
  24.   
  25. public class CustomerDAL1 : ICustomerDAL    
  26. {    
  27.     public string GetString()    
  28.     {    
  29.         return "Hello World...1";    
  30.     }    
  31. }    
  32.   
  33. public class CustomerBL    
  34. {    
  35.     public string GetDALString()    
  36.     {    
  37.         //Calling CustomerDAL    
  38.         ICustomerDAL customerObject = Factory.InstantiateCustomer();    
  39.         return customerObject.GetString();    
  40.     }    
  41. }    
Now you can see CustomerBL is loosely-coupled with CustomerDAL. We just need to change only Factory class and rest of the classes will be unchanged.

This pattern is known as static Factory Pattern. But you can see there is a problem in static Factory Pattern, that the Factory class is responsible for initiating object. So we are tightly-coupled with Factory class. Now if we want to change factory during runtime then it is not possible.

Abstract Factory Pattern

This problem is already solved by an abstract Factory Pattern that is nothing but one more level of inheritance. We will make an interface for the factory also. See the following example.
  1. public interface ICustomerDAL  
  2. {  
  3.     string GetString();  
  4. }  
  5.   
  6. public interface IFactory  
  7. {  
  8.     ICustomerDAL InstantiateCustomer();  
  9. }  
  10.   
  11. public class FactoryManager  
  12. {  
  13.   
  14.     private static Dictionary<String, IFactory> factories =  
  15.         new Dictionary<String, IFactory>();  
  16.   
  17.     public static void addFactory(String factoryId, IFactory factory)  
  18.     {  
  19.         factories.Add(factoryId, factory);  
  20.     }  
  21.   
  22.     public static IFactory getFactory(String factoryId)  
  23.     {  
  24.         IFactory factoryObject;  
  25.         factories.TryGetValue(factoryId, out factoryObject);  
  26.         return factoryObject;  
  27.     }  
  28. }  
  29.   
  30. public class FactoryInitialization  
  31. {  
  32.     public void Initialiize()  
  33.     {  
  34.         FactoryManager.addFactory("CustomerFactory"new Factory());  
  35.     }  
  36. }  
  37.   
  38. public class Factory : IFactory  
  39. {  
  40.     public ICustomerDAL InstantiateCustomer()  
  41.     {  
  42.         if (true//Condition here  
  43.             return new CustomerDAL();  
  44.         else  
  45.             return new CustomerDAL1();  
  46.     }  
  47. }  
  48.   
  49. public class CustomerDAL : ICustomerDAL  
  50. {  
  51.     public string GetString()  
  52.     {  
  53.         return "Hello World";  
  54.     }  
  55. }  
  56.   
  57. public class CustomerDAL1 : ICustomerDAL  
  58. {  
  59.     public string GetString()  
  60.     {  
  61.         return "Hello World...1";  
  62.     }  
  63. }  
  64.   
  65.   
  66. public class CustomerBL  
  67. {  
  68.     public string GetDALString()  
  69.     {  
  70.         //Calling CustomerDAL  
  71. tomerDAL customerObject = FactoryManager.getFactory("CustomerFactory").InstantiateCustomer();  
  72.         return customerObject.GetString();  
  73.     }  
  74. }  
But in an abstract Factory Pattern there is the problem that the factoryId is hard-coded. To resolve this problem we will use Dependency Injection.

Dependency Injection: When using Dependency Injection the responsibility of obtaining the necessary instances is reversed. A class no longer obtains these instances itself. Instead, a Dependency Injection container creates the needed instances and "injects" them into the object. There are the following tree types of injections possible.

Constructor injection: The dependencies are provided by a class constructor. This is a technique of passing an object's dependencies to its constructor.
  1. public interface ICustomerClass  
  2. {  
  3.     string GetString();  
  4. }  
  5.   
  6. public class ConstructorInjection  
  7. {  
  8.     private ICustomerClass _customerObject;  
  9.   
  10.     //Constructor  
  11.     public ConstructorInjection(ICustomerClass customerObject)  
  12.     {  
  13.         _customerObject = customerObject;  
  14.     }  
  15.     public string GetDALString()  
  16.     {  
  17.         //Calling CustomerClass  
  18.         return _customerObject.GetString();  
  19.     }  
  20. }  
Setter injection: The client exposes a setter method that the injector uses to inject the dependency.
  1. public interface ICustomerClass    
  2. {    
  3.     string GetString();    
  4. }    
  5.   
  6. public class SetterInjection    
  7. {    
  8.     private ICustomerClass _customerObject;    
  9.   
  10.     // Setter Method, Set the object to be used by this client    
  11.     public void SetterMethod(ICustomerClass customerObject)    
  12.     {    
  13.         _customerObject = customerObject;    
  14.     }    
  15.     public string GetString()    
  16.     {    
  17.         //Calling CustomerClass    
  18.         return _customerObject.GetString();    
  19.     }    
  20. }     
Interface injection: The dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.
  1. public interface ICustomerClass  
  2. {  
  3.     string GetString();  
  4. }  
  5.   
  6. public interface IInterfaceInjection   
  7. {  
  8.     void SetterMethod(ICustomerClass customerObject);  
  9. }  
  10.   
  11. public class InterfaceInjection : IInterfaceInjection  
  12. {  
  13.     private ICustomerClass _customerObject;  
  14.   
  15.     // Setter Method, Set the object to be used by this client  
  16.     public void SetterMethod(ICustomerClass customerObject)  
  17.     {  
  18.         _customerObject = customerObject;  
  19.     }  
  20.     public string GetString()  
  21.     {  
  22.         //Calling CustomerClass  
  23.         return _customerObject.GetString();  
  24.     }  
  25. }  
Unit testing using Dependency Injection

Since we implement Dependency Injection it will become very easy to test a unit of code individually. We will just mock or stub the dependent object to test a unit of code individually.

Example: First I am implementing the code that will be tested as in the following:
  1. public interface ICustomerDataAccessLayer    
  2. {       
  3.     int Calculate(int x);    
  4. }    
  5. public class CustomerDataAccessLayer : ICustomerDataAccessLayer    
  6. {    
  7.     public int Calculate(int x)    
  8.     {    
  9.         return x * 2;    
  10.     }    
  11. }    
  12.   
  13. public class CustomerBusinessService    
  14. {    
  15.     private ICustomerDataAccessLayer _customerObject;    
  16.     public CustomerBusinessService(ICustomerDataAccessLayer customerObject)    
  17.     {    
  18.         _customerObject = customerObject;    
  19.     }    
  20.     public int CalculateAmount(int x, int y)    
  21.     {    
  22.         int z = y * y; //Want to test these logic independently    
  23.         return z + _customerObject.GetString();    
  24.     }    
  25. }  
Now I am implementing a unit test for the preceding code. I want to test only the CalculateAmount() method without invoking the Calculate() method. So I need to stub the ICustomerDataAccessLayer interface.
  1. using System;  
  2. using Microsoft.VisualStudio.TestTools.UnitTesting;  
  3. using DependencyInjection.Fakes;  
  4. using DependencyInjection;  
  5. namespace UnitTestProject1  
  6. {  
  7.     [TestClass]  
  8.     public class UnitTest1  
  9.     {  
  10.         [TestMethod]  
  11.         public void TestCalculateAmount()  
  12.         {  
  13.             var stubbedObject = new StubICustomerDataAccessLayer()  
  14.             {  
  15.                 CalculateInt32 = (x) =>  
  16.                 {  
  17.                     return 0;  
  18.                 }  
  19.             };  
  20.   
  21. var actualResult= new CustomerBusinessService(stubbedObject).CalculateAmount(2, 3);  
  22.            Assert.AreEqual<int>(9, actualResult);  
  23.         }  
  24.     }  
  25. }  
In the preceding example I stubbed the ICustomerDataAccessLayer and tested the CustomerBusinessService.CalculateAmount(x,y) method independently.

I hope you enjoyed this article. If you have any query or remarks then please comment, I will be with you.

Thank you.

Up Next
    Ebook Download
    View all
    Learn
    View all