Dependency Inversion

Definition of Dependency inversion principal is (DIP) : There are 2 parts to understand:

First Part: High-level modules should not depend on low-level modules. Both should depend on abstractions.

Second Part: Abstractions should not depend on details. Details should depend on abstractions.

In English, abstraction means something which is non-concrete. So, abstraction in programming world is to create an interface or abstract class which is non-concrete.

Understanding first part: "High-level modules should not depend on low-level modules. Both should depend on abstractions." A high-level class is a class that does something significant in the application, while a low-level class is a class that does some auxiliary work. Let’s take an example. Let's say we have module to manage users, login credentials and roles. As a part of user management, a way to a change password is required. When a password is changed, a notification is to be sent to the user about the change. In this case the class doing the user management is the high-level class, and the class sending notification is a low-level class.

So point to remember is: We should not use concrete low-level classes inside a high-level class, because then the high-level class becomes tightly coupled with those low-level classes. Tomorrow if any of the low-level classes change, the high-level class may break. As per DIP, the high-level classes should depend on abstraction (in the form of abstract classes or interfaces) and so should the low-level classes. The tight coupling is removed by coding both levels of classes against interfaces.

Fig 1: UserManager class is directly depends upon EmailNotifier Class.

Fig 2: UserManager class no longer uses E mailNotifier directly. Instead, it depends on interface—INotifier. INotifier interface is implemented by the EmailNotifier class or any other classes to send out different types of notifications.

INotifier is injected via constructors to UserManager class.

Understanding second part: "Abstractions should not depend on details. Details should depend on abstractions." It means we should design INotifier interface (Abstraction) depending upon the needs of UserManager and not on the needs of EmailNotifier (details).

By thinking this way we were able to identify that a notifications may not be just restricted to email but it is possible that tomorrow we may SMS notifications also.

The Dependency Inversion Principle helps us understand how to correctly bind system together. It tells us to have implementation detail depend on the higher-level policy abstractions, and not the other way around. This helps us move toward a system that is coupled correctly, and directly influences that system’s encapsulation and cohesion.

Conclusion:

Direct Dependency Graph

Inverted Dependency Graph

The practice of dependency injection is made possible by following the dependency inversion principle.