The Dependency Inversion Principle (DIP) states that a high-level class must not depend upon a lower level class. They must both depend upon abstractions. And, secondly, an abstraction must not depend upon details, but the details must depend upon abstractions. This will ensure the class and ultimately the whole application is very robust and easy to maintain and expand
// Not following the Dependency Inversion Principle
blic class SalaryCalculator
{
public float CalculateSalary(int hoursWorked, float hourlyRate) => hoursWorked * hourlyRate;
}
public class EmployeeDetails
{
public int HoursWorked { get; set; }
public int HourlyRate { get; set; }
public float GetSalary()
{
var salaryCalculator = new SalaryCalculator();
return salaryCalculator.CalculateSalary(HoursWorked, HourlyRate);
}
}
These classes do not follow the “Dependency Inversion Principle” as the higher-level class EmployeeDetails is directly depending upon the lower level SalaryCalculator class.
We can fix this issue as below:
// Following the Dependency Inversion Principle
public interface ISalaryCalculator
{
float CalculateSalary(int hoursWorked, float hourlyRate);
}
public class SalaryCalculatorModified : ISalaryCalculator
{
public float CalculateSalary(int hoursWorked, float hourlyRate) => hoursWorked * hourlyRate;
}
public class EmployeeDetailsModified
{
private readonly ISalaryCalculator _salaryCalculator;
public int HoursWorked { get; set; }
public int HourlyRate { get; set; }
public EmployeeDetailsModified(ISalaryCalculator salaryCalculator)
{
_salaryCalculator = salaryCalculator;
}
public float GetSalary()
{
return _salaryCalculator.CalculateSalary(HoursWorked, HourlyRate);
}
}
In the above code, we see that we have created an interface ISalaryCalculator and then we have a class called SalaryCalculatorModified that implements this interface. Finally, in the higher-level class EmployeeDetailsModified, we only depend upon the ISalaryCalculator interface and not the concrete class.