Last principle stating that high-level modules should not be dependent low-level modules, and their dependencies should rely on abstraction by interfaces or abstract classes. In simple terms – we should minify dependencies from certain implementations. The best way to achieve it, is to rely on interfaces – then our code has small amount of dependencies, and as we know, interfaces are stable – it means, that if we do any changes to our interface, this change will be related with changes in interface implementation. Lets move on to wrong code, where high-module depends on low-level module:
public class Employee
{
public string Name { get; set; }
}
public class EmployeeRepository
{
public void Add(Employee employee)
{
//some code to add
}
}
public class EmployeeService
{
private EmployeeRepository _employeeRepository = new EmployeeRepository();
public void Add(Employee employee)
{
_employeeRepository.Add(employee);
}
}
To start with, we have to define some basics, which are high-level modules and low-level modules. In example displayed above, EmployeeService is high-level module class, and low-level module is EmployeeRepository class. High-level module in this case is dependent on low-level module, because it uses implementation of EmployeeRepository class, which is obviously breaking spoken principle. To make this code correct, there is a need to „switch” this dependency, just like the name of principle states:
public class Employee
{
public string Name { get; set; }
}
//created interface with one method implemented
public interface IEmployeeRepository
{
void Add(Employee employee);
}
// EmployeeRepository class now inherits IEmployeeRepository interface
public class EmployeeRepository : IEmployeeRepository
{
public void Add(Employee employee)
{
//some code to add
}
}
//created constructor, which injects dependency
public class EmployeeService
{
private IEmployeeRepository _employeeRepository;
public EmployeeService(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
public void Add(Employee employee)
{
_employeeRepository.Add(employee);
}
}
Correct code above, our class EmployeeService does not depend on actual implementation of EmployeeRepository, but on its abstraction – in this case, IEmployeeRepository interface. Changes made in low-level module does not affect on high-level module, thanks to that we could „switch” our dependencies.