Dependency Inversion Principle

Before going through this article, I strongly recommend reading my previous articles.

Dependency Inversion Principle

The Dependency Inversion Principle is one of the SOLID principles defined by Robert C. Martin. This principle is about dependencies among the components (such as two modules, and two classes) of the software.

The principle says that high-level modules should depend on abstraction, not on the details, of low-level modules, in other words not the implementation of the low-level module. Abstraction should not depend on details. Details should depend on abstraction. In simple words the principle says that there should not be a tight coupling among components (in other words two modules, two classes) of software and to avoid that, the components should depend on abstraction, in other words, a contract (interface or abstract class).

Dependency Inversion Principle in Real life

To understand the second problem better way, let's see a real-life scenario of a computer or laptop.

Different Port

Figure 1. Various Port

As you can see in the preceding image we have a port for each external device to which I can associate an external device and do our work.

But the problem with this is, that I cannot attach my keyboard to a printer port and vice versa. The same problem occurs with the other devices. So this is like a tight coupling, that I cannot change my external device on the given interface, in other words on which I depend.

The solution to this is a USB port.

If I have a USB port then I can easily attach any device to my machine and perform my task.

USB port

Figure 2. USB port

Example of Dependency Inversion Principle in Application Development

The following is a class diagram of tight coupling that does not follow the principle.

class diagram of tight coupling

Figure 3. Class diagram of tight coupling

Public Class Customer  
{  
    CustomerRepository CustomerRepository;  
    Public Customer  
    {  
        CustomerRepository = new CustomerRpository();  
}  
  
Public bool Save()  
{  
    CustomerRepository.Save();  
}  
}  
  
Public class CustomerRepository  
{  
    Public bool Save(dattype data)  
{  
              //Sql Connection object and Save data in Sql server   
}  
}  

The preceding code is tightly coupled because the current repository deals with the SQL server. So if the requirement is to use an Oracle server then there is modification required for the Customer class.

So to avoid that, make the customer class depend on abstraction. The following is an image of a class diagram where the customer depends on abstraction and supports both SQL and Oracle servers.

Class diagram

Figure 4. Class diagram where the customer depends on abstraction

Public Class Customer   
{  
    CustomerRepository CustomerRepository;  
    Public Customer   
    {  
        CustomerRepository = new CustomerRpository();  
    }  
  
    Public bool Save()   
    {  
        CustomerRepository.Save();  
    }  
}  
  
Public class CustomerRepository   
{  
    Public bool Save(dattype data)   
    {  
        //Sql Connection object and Save data in Sql server     
    }  
}  

So in the preceding code, the customer class depends on ICustomerRepository abstraction, in other words, an interface. Another thing is here the customer class receives a dependency via consumption of the customer class or using a dependency container.

Note. Here is an example of the class but the same goes for the modules designed in software because dependency inversion is about providing a set of abstraction policies on which the details depend and the policy that provides flexibility in the software system.

Disadvantages of the Dependency Inversion Principle

Application modules become tightly coupled, which means.

  1. The testability of the module becomes difficult.
  2. Parallel development of the module becomes difficult.
  3. Many changes are required when there is a modification in the module and when there are changes in the module it depends on.