Software Engineering Principle: Dependency Inversion Principle (DIP)

Neo Kai Jun
3 min readOct 23, 2020

Hello everyone, today I am going to share with you an indispensable software engineering principle called the Dependency Inversion Principle or DIP for short. It is also known as Inversion of Control or Dependency Injection. Its definition is as follows:

High level modules should not depend on low level modules. Instead, both modules should depend on abstractions.

The benefit of DIP lies in the decoupling of high and low level classes. That is, when the low-level class changes in its implementation, or when more low-level classes are added, the high-level class will not be affected and need not update itself to reflect any low-level changes.

Figure illustrating Dependency Inversion Principle (DIP)

The left class diagram (without DIP) shows an association from class High to class Middle and class Middle to class Low; clearly the higher level class depends on the lower level.

However, for the right class diagram (with DIP), we do not see such behavior. Instead, both class High and class Low depend on an abstraction i.e. HighService interface. With that, if class Low changes in its implementation, class Middle would not be affected since the interface MiddleService remains unchanged.

Let us observe the usage of DIP in a concrete example. Let’s suppose we are building an online grocery store application for our client. Our shopping cart module is illustrated below:

Original implementation

Here, we can see that ShoppingCart is at a higher level than Beverage. The Beverage class allows users to see the reviews and get the price of each beverage they added onto their ShoppingCart. The ShoppingCart allows them to add a beverage to the shopping cart and checkout once they are done shopping.

Now, what happens if our client does not want to sell only beverages, but also snacks? In this case, we would have to do the following:

We will have to create a new Snack class, and change the ShoppingCart class to support the addition of Snack. This change is required because of the coupling between ShoppingCart and its lower classes (i.e. the products). This would clearly violate the Open-Closed Principle (OCP) as we now have to modify ShoppingCart to extend the app.

So what can we do? Our solution is to re-design our Shopping Cart module as follows:

Re-designed implementation

Our solution above creates an abstraction layer (Product interface) for the lower-level class (Beverage class) to implement. In so doing, we eliminated the dependency of the high-level ShoppingCart with the low-level Beverage, decoupling both classes as a result.

Now, what happens if our client does not want to sell only beverages, but also snacks? In this case, we can simply do the following:

Updated implementation to include snacks

As can be seen, all we need to do is to create a Snack class and have it implement the Product interface. And that’s it! The ShoppingCart will support the addition of Snack through polymorphism, without us having to change it at all! This is thanks to the decoupling effected by DIP. In fact, if our client changes his mind and decides to sell cereals instead of snacks, or even decides to sell more genres of food (fruits, candies, dairy…), our ShoppingCart will still be able to support them without having to change it.

That’s all for now. I hope you have learnt something new about Dependency Inversion Principle, and is now ready to apply it in your projects. Till next time!

--

--