SOLID Principles – Dependency inversion principle
In object-oriented design, the dependency inversion principle is a specific methodology for loosely coupling software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details.
from Wiki
The dependency inversion principle(DIP) states:
- High-level modules should not import anything from low-level modules. Both should depend on abstractions or interfaces.
- Abstractions should not rely on details, and concrete implementations should depend on abstractions or interfaces.
Both high-level and low-level objects must depend on the same abstractions or interfaces; this design principle inverts the way people think about object-oriented programming(OOP).
The idea behind points A and B of this principle is that when designing the interaction between the high-level modules and the low-level ones, the interaction behavior should be considered an abstract interaction or the interface interfaces between them. It doesn’t only has implications for the design of the high-level module. On the other hand, the low-level modules should be designed with the interaction in mind, and it may be necessary to change their user interface.
Traditional layers pattern
The traditional layer pattern is sometimes called “Normal flow” or “Ordinal flow,” which all describe the traditional layer pattern as a kind of word. In the conventional application architecture, the low-level components are designed to be consumed by the higher-level components, which enable increasingly complex systems to be built. The higher-level components depend directly upon lower-level components to achieve some tasks, which dependency upon lower-level components limits reuse opportunities of the higher-level components.

Dependency inversion pattern
The dependency inversion pattern(DIP) goal is to avoid this highly coupled distribution with the mediation of an abstract layer and to increase the re-usability of higher layers or the re-usability of policy layers.
Higher- and lower-level layers reduce the traditional dependencies from top to bottom with the abstract layer addition. Nevertheless, the “inversion” concept doesn’t mean that the lower-level layers depend on the higher-level layers directly; both layers should depend on abstractions or interfaces that expose the behavior needed by the higher-level layers. The abstracts and the interfaces are owned by the upper layers or the policy layers. This program architecture groups the higher components, the policy components, abstractions and interfaces that define lower services together in the same packages. The lower-level layers are created by the inheritance or the implementation of these abstract class objects or interfaces.
The inversion of the dependencies and ownership encourages the re-usability of the higher and policy layers. Upper layers could use other implementations of the lower services. When the programs suddenly close lower-level layer components or applications that require the reuse of existing services, it is expected that an adapter mediates between the services and the abstractions.
In the modern web application of the Microsoft .NET SDK, most developers will use dependency injection(DI) to implement it. Other computer languages also do the same things, focusing on everyone’s request lifetime management. DIP has become the ordinary modern skill in the web application program that even you are a junior level programmer or a senior programmer and software architecture should know how to use and implement.


How to implement the dependency inversion pattern(DIP)?
public void Solution()
{
Console.WriteLine("Dependency Inversion Principle");
var empManager = new EmployeeManager();
empManager.AddEmployee(new Employee { Name = "Leen", Gender = Gender.Female, Position = Position.Manager });
empManager.AddEmployee(new Employee { Name = "Mike", Gender = Gender.Male, Position = Position.Amdinistrator });
var stats = new EmployeeStatistics(empManager);
Console.WriteLine($"Number of female managers in our company is: {stats.CountFemaleManager()}");
}
internal enum Gender
{
Male,
Female
}
internal enum Position
{
Amdinistrator,
Manager,
Executive
}
internal interface IEmployeeSearchable
{
IEnumerable<Employee> GetEmployeesByGenderAndPosition(Gender gender, Position position);
}
internal class Employee
{
public string Name { get; set; } = string.Empty;
public Gender Gender { get; set; }
public Position Position { get; set; }
}
internal class EmployeeManager : IEmployeeSearchable
{
private readonly List<Employee> _employees = new List<Employee>();
public EmployeeManager()
{ }
public void AddEmployee(Employee employee) => _employees.Add(employee);
public IEnumerable<Employee> GetEmployeesByGenderAndPosition(Gender gender, Position position)
=> _employees.Where(emp => emp.Gender.Equals(gender) && emp.Position.Equals(position));
public List<Employee> Employees => _employees;
}
internal class EmployeeStatistics
{
private readonly IEmployeeSearchable _emp;
public EmployeeStatistics(IEmployeeSearchable emp)
=> _emp = emp;
public int CountFemaleManager()
=> _emp.GetEmployeesByGenderAndPosition(Gender.Female, Position.Manager).Count();
}
Reference
- Dependency inversion principle – Wiki
- Dependency inversion principle
- SOLID Design Principles Explained: Dependency Inversion Principle with Code Examples