Before going through this article, I strongly recommend reading my previous articles:
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, 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.
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, 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.
Solution to this is USB port.
If I have a USB port then I can easily attach any device to my machine and perform my task.
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.
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)
- {
-
- }
- }
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.
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)
- {
-
- }
- }
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 consumtion 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
Application modules become tightly coupled, that means:
- The testability of the module becomes difficult.
- Parallel development of the module becomes difficult.
- Many changes are required when there is modification in the module and when there are changes in the module it depends on.
Read more Dependency Injection talks about the disadvantages and advantages of using dependency inversion.
Note: Dependency Injection is not the same as Dependency Inversion because Dependency Inversion is about defining an abstraction policy for the software module whereas Dependency Injection is a set of patterns to supply dependency.