SOLID CODE: Dependency Inversion Principle

To become a senior among millions of programmers!

Photo by Thom Milkovic on Unsplash

Introduction

Welcome to the final article in the SOLID series. In this article, I will talk about the Dependency Inversion Principle.

  1. Single Responsibility Principle
  2. Open / Closed Principle
  3. Liskov Substitution Principle
  4. Interface Segregation Principle
  5. Dependency Inversion Principle

Principle content

1. High-level modules should not depend on low-level modules. Both should depend on the abstraction.
2. Interface (abstraction) should not depend on details, but vice versa. (Classes communicate with each other through interfaces, not through the implementation.)

Explain the principle

In the article, I often use the word module. In fact, this module can be a project, a DLL file, or a service. To make it easy to understand, you should consider each module as a class just in this article.

With conventional coding, the top-level modules will call the low-level modules. The top-level module will be dependent, and the low-level module creates the dependencies. When the low-level module changes, the top-level module must change accordingly. One change entails a series of changes, reducing the maintainability of the code.

dependencies

If it follows a DIP, both the low-level and the high-level modules are dependent on a constant interface. We can easily replace, modify low-level modules without affecting the top-level modules.

To make it easy to understand, take a look at the electric lights in your home. The high-level module is the power outlet; the main interface is the round light tail; the low-level modules are the round bulb and the fluorescent bulb.

These two modules all inherit the circular chasing interface; We can easily change the 2 types of bulbs because the high-level module (power socket) only cares about the interface (round tail), does not care about the implementation (bulb or fluorescent. ).

Photo by Patrick Schneider on Unsplash

Also, in the code, when applying a DIP, the modules are linked through the interface. To connect to the database, we need to call the Get, Save … function of the IDataAccess interface. When replacing the database, we need to change the implementation of this interface.

Example

Code when not applied DIP:

// Cart is a high level module
public class Cart
{
public void Checkout (int orderId, int userId)
{
// Database, Logger, EmailSender is the visibility module
Database db = new Database ();
db.Save (orderId);

Logger log = new Logger ();
log.LogInfo ("Order has been checkout");

EmailSender es = new EmailSender ();
es.SendEmail (userId);
}
}

Code after the redesign, apply a DIP:

In practice, people often apply the Dependency Injection pattern to ensure the DIP principle in the code.

Conclusion

DIP is used the most in code, but it is also controversial. Besides some advantages, DIP also comes with some disadvantages:

  • Reduced cohesion between modules
  • The code is easy to maintain, easy to replace the module.
  • It’s easy to test and write a Unit Test.
  • The concept of DI is quite “indigestible”; new developers will have difficulty learning.
  • Using the interface can sometimes be difficult to debug, as it is not known exactly which module is called.
  • Increased code complexity

References

Always be nice to anybody who has access to my toothbrush.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store