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.
- public class CustomerBL
- {
- public string GetDALString()
- {
- //Calling CustomerDAL
- CustomerDAL customerObject = new CustomerDAL();
- return customerObject.GetString();
- }
- }
-
- public class CustomerDAL
- {
- public string GetString()
- {
- return "Hello World";
- }
- }
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.
- public class CustomerDAL1
- {
- public string GetString()
- {
- return "Hello World…1";
- }
- }
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.
- public interface ICustomerDAL
- {
- string GetString();
- }
-
- public class CustomerDAL : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World";
- }
- }
-
- public class CustomerDAL1 : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World...1";
- }
- }
-
- public class CustomerBL
- {
- private ICustomerDAL _customerObject;
-
- public CustomerBL()
- {
- _customerObject = new CustomerDAL();
- }
- public CustomerBL(bool condition)
- {
- if (condition)
- _customerObject = new CustomerDAL();
- else
- _customerObject = new CustomerDAL1();
-
- }
- public string GetDALString()
- {
- return _customerObject.GetString();
- }
- }
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.
- public interface ICustomerDAL
- {
- string GetString();
- }
-
- public class Factory
- {
- public static ICustomerDAL InstantiateCustomer()
- {
- if (true)
- return new CustomerDAL();
- else
- return new CustomerDAL1();
- }
- }
-
- public class CustomerDAL : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World";
- }
- }
-
- public class CustomerDAL1 : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World...1";
- }
- }
-
- public class CustomerBL
- {
- public string GetDALString()
- {
-
- ICustomerDAL customerObject = Factory.InstantiateCustomer();
- return customerObject.GetString();
- }
- }
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.
- public interface ICustomerDAL
- {
- string GetString();
- }
-
- public interface IFactory
- {
- ICustomerDAL InstantiateCustomer();
- }
-
- public class FactoryManager
- {
-
- private static Dictionary<String, IFactory> factories =
- new Dictionary<String, IFactory>();
-
- public static void addFactory(String factoryId, IFactory factory)
- {
- factories.Add(factoryId, factory);
- }
-
- public static IFactory getFactory(String factoryId)
- {
- IFactory factoryObject;
- factories.TryGetValue(factoryId, out factoryObject);
- return factoryObject;
- }
- }
-
- public class FactoryInitialization
- {
- public void Initialiize()
- {
- FactoryManager.addFactory("CustomerFactory", new Factory());
- }
- }
-
- public class Factory : IFactory
- {
- public ICustomerDAL InstantiateCustomer()
- {
- if (true)
- return new CustomerDAL();
- else
- return new CustomerDAL1();
- }
- }
-
- public class CustomerDAL : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World";
- }
- }
-
- public class CustomerDAL1 : ICustomerDAL
- {
- public string GetString()
- {
- return "Hello World...1";
- }
- }
-
-
- public class CustomerBL
- {
- public string GetDALString()
- {
-
- tomerDAL customerObject = FactoryManager.getFactory("CustomerFactory").InstantiateCustomer();
- return customerObject.GetString();
- }
- }
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.
- public interface ICustomerClass
- {
- string GetString();
- }
-
- public class ConstructorInjection
- {
- private ICustomerClass _customerObject;
-
-
- public ConstructorInjection(ICustomerClass customerObject)
- {
- _customerObject = customerObject;
- }
- public string GetDALString()
- {
-
- return _customerObject.GetString();
- }
- }
Setter injection: The client exposes a setter method that the injector uses to inject the dependency.
- public interface ICustomerClass
- {
- string GetString();
- }
-
- public class SetterInjection
- {
- private ICustomerClass _customerObject;
-
-
- public void SetterMethod(ICustomerClass customerObject)
- {
- _customerObject = customerObject;
- }
- public string GetString()
- {
-
- return _customerObject.GetString();
- }
- }
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.
- public interface ICustomerClass
- {
- string GetString();
- }
-
- public interface IInterfaceInjection
- {
- void SetterMethod(ICustomerClass customerObject);
- }
-
- public class InterfaceInjection : IInterfaceInjection
- {
- private ICustomerClass _customerObject;
-
-
- public void SetterMethod(ICustomerClass customerObject)
- {
- _customerObject = customerObject;
- }
- public string GetString()
- {
-
- return _customerObject.GetString();
- }
- }
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:
- public interface ICustomerDataAccessLayer
- {
- int Calculate(int x);
- }
- public class CustomerDataAccessLayer : ICustomerDataAccessLayer
- {
- public int Calculate(int x)
- {
- return x * 2;
- }
- }
-
- public class CustomerBusinessService
- {
- private ICustomerDataAccessLayer _customerObject;
- public CustomerBusinessService(ICustomerDataAccessLayer customerObject)
- {
- _customerObject = customerObject;
- }
- public int CalculateAmount(int x, int y)
- {
- int z = y * y;
- return z + _customerObject.GetString();
- }
- }
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.
- using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using DependencyInjection.Fakes;
- using DependencyInjection;
- namespace UnitTestProject1
- {
- [TestClass]
- public class UnitTest1
- {
- [TestMethod]
- public void TestCalculateAmount()
- {
- var stubbedObject = new StubICustomerDataAccessLayer()
- {
- CalculateInt32 = (x) =>
- {
- return 0;
- }
- };
-
- var actualResult= new CustomerBusinessService(stubbedObject).CalculateAmount(2, 3);
- Assert.AreEqual<int>(9, actualResult);
- }
- }
- }
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.