$$ \newcommand\Tr{\mathrm{Tr}} \newcommand{\braket}[2]{\langle #1 \mid #2 \rangle} \newcommand\I{\mathbb{I}} \newcommand{\avg}[1]{\left< #1 \right>} \newcommand{\RD}{D} \newcommand{\ri}{\mathrm{i}} \DeclareMathOperator{\sign}{sign} \DeclareMathOperator{\Sign}{Sign} \newcommand{\ii}{\mathrm i} \newcommand{\vv}{\mathrm v} \newcommand{\ff}{\mathrm f} \newcommand{\mm}{\mathrm m} \newcommand{\ee}{\mathrm e} \newcommand{\xx}{\mathrm x} \newcommand{\RR}{\mathrm R} \newcommand{\dd}{\mathrm d} \newcommand{\FF}{\mathrm F} \newcommand{\BB}{\mathrm B} \newcommand{\vph}{v_{\mathrm{ph}}} $$

Dependency Injection

What is Dependency Injection

Dependency Injection is one of five SOLID principles that states a class should receive objects that it depends on rather than creating it inside the class.

In the implementation, Dependency Injection also suggests to decouple the codes through abstractions such as interface, abstract class or inheritance.

Below is a code sample that doesn’t apply Dependency Injection.

public class LeaveProcessor
{
    private LeaveEligibilityChecker leaveEligibilityChecker;

    public LeaveProcessor()
    {
        leaveEligibilityChecker = new LeaveEligibilityChecker();
    }

    public string ProcessLeaves(Employee employee)
    {
        bool eligible = leaveEligibilityChecker.Eligible(employee);
        if (eligible)
        {
            return "The employee is eligible to take leave.";
        }

        return "The employee is not eligible to take leave.";
    }
}

public class LeaveEligibilityChecker
{
    private const int MaxLeave = 12;

    public bool Eligible(Employee employee)
    {
        if (employee.Leaves < MaxLeave)
            return true;

        return false;
    }
}

public class Employee
{
    public string Name { get; private set; }
    public int Leaves { get; private set; }

    public Employee(string name, int leaves)
    {
        Name = name;
        Leaves = leaves;
    }
}

How to apply Dependency Injection

Below is the code that applies Dependency Injection. In this case, it uses constructor injection in LeaveProcessor class.

We do these modifications:

  1. Add interface ILeaveEligibilityChecker so different concrete class of this interface can be used at runtime
  2. Let LeaveEligibilityChecker implements ILeaveEligibilityChecker interface
  3. Change LeaveProcessor constructor by adding ILeaveEligibilityChecker parameter, then set _leaveEligibilityChecker variable to the newly added ILeaveEligibilityChecker parameter

public class LeaveProcessor
{
    private readonly ILeaveEligibilityChecker _leaveEligibilityChecker;

    public LeaveProcessor(ILeaveEligibilityChecker leaveEligibilityChecker)
    {
        _leaveEligibilityChecker = leaveEligibilityChecker;
    }

    public string ProcessLeaves(Employee employee)
    {
        bool eligible = _leaveEligibilityChecker.Eligible(employee);
        if (eligible)
        {
            return "The employee is eligible to take leave.";
        }

        return "The employee is not eligible to take leave.";
    }
}
public interface ILeaveEligibilityChecker
{
    bool Eligible(Employee employee);
}

public class LeaveEligibilityChecker : ILeaveEligibilityChecker
{
    private const int MaxLeave = 12;

    public bool Eligible(Employee employee)
    {
        if (employee.Leaves < MaxLeave)
            return true;

        return false;
    }
}

public class Employee
{
    public string Name { get; private set; }
    public int Leaves { get; private set; }

    public Employee(string name, int leaves)
    {
        Name = name;
        Leaves = leaves;
    }
}

Why do we need Dependency Injection

Even though in theory, Dependency Injection leads us to loosely coupled code through abstractions, the real benefit of Dependency Injection is in unit testing where it enables us to mock the dependencies.

In below example, we use Moq as mocking framework to control the behavior of Eligible method regardless the passed argument. We want to test if the employee is eligible to take leave, then the ProcessLeaves method must return a message stating that the employee is eligible to take leave.

[TestMethod()]
public void ProcessLeavesTest()
{
    // Arrange
    var leaveEligibilityChecker = new Mock<ILeaveEligibilityChecker>();
    leaveEligibilityChecker.Setup(x => x.Eligible(It.IsAny<Employee>())).Returns(true);

    // Act
    LeaveProcessor leaveProcessor = new LeaveProcessor(leaveEligibilityChecker.Object);
    string actual = leaveProcessor.ProcessLeaves(new Employee("Michael", 11));

    // Assert
    string exptected = "The employee is eligible to take leave.";
    Assert.AreEqual(exptected, actual);
}

Actually, the Eligible method above is quite simple that we might not need to mock. But, if that method connects to database or API then we have to mock it because an unit test should not connect to database.

Types of Dependency Injection

There are three types of dependency injection:

  1. Constructor Injection
    We inject all dependencies through constructor like we did in previous section.

    The advantages of Constructor Injection:

    • We can make dependencies to be immutable by assign them to readonly variables in the constructor. This ensures they must have been initialized and can be modified only in the constructor.

    The disadvantages of Constructor Injection:

    • All dependencies must be provided upfront which is less flexible.

  2. Setter Injection
    We provide setter method to inject dependencies.
    Previous LeaveProcessor class could be rewritten as below.

    public class LeaveProcessor
    {
        private ILeaveEligibilityChecker _leaveEligibilityChecker;
    
        public void SetLeaveEligibilityChecker(ILeaveEligibilityChecker leaveEligibilityChecker)
        {
            _leaveEligibilityChecker = leaveEligibilityChecker;
        }
    
        public string ProcessLeaves(Employee employee)
        {
            bool eligible = _leaveEligibilityChecker.Eligible(employee);
            if (eligible)
            {
                return "The employee is eligible to take leave.";
            }
    
            return "The employee is not eligible to take leave.";
        }
    }
    

    There are some disadvantages of Setter Injection:

    • It’s difficult to ensure that the depencencies have been initialized or not
    • It allows dependencies to be mutable if the original passed dependencies are not immutable objects because it can be manipulated anywhere in the class
  3. Interface Injection

    public class ServiceInjector
    {
        public void Inject(ILeaveEligibilityCheckSetter leaveEligibilityCheckSetter)
        {
            leaveEligibilityCheckSetter.SetLeaveEligibilityChecker(new LeaveEligibilityChecker());
        }
    }
    
    public interface ILeaveEligibilityCheckSetter
    {
        void SetLeaveEligibilityChecker(ILeaveEligibilityChecker leaveEligibilityChecker);
    }
    
    public class LeaveProcessor : ILeaveEligibilityCheckSetter
    {
        private ILeaveEligibilityChecker _leaveEligibilityChecker;
    
        public void SetLeaveEligibilityChecker(ILeaveEligibilityChecker leaveEligibilityChecker)
        {
            _leaveEligibilityChecker = leaveEligibilityChecker;
        }
    
        public string ProcessLeaves(Employee employee)
        {
            bool eligible = _leaveEligibilityChecker.Eligible(employee);
            if (eligible)
            {
                return "The employee is eligible to take leave.";
            }
    
            return "The employee is not eligible to take leave.";
        }
    }
    

Whicy type of Dependency Injection is better

Constructor Injection is better because we can make our dependecies to be immutable by assign them to readonly variables. It’s important especially in multithreading environment where the shared state should be as immutable as possible to make it less error prone.