Mastering Dependency Inversion Principle (DIP)

Mastering Dependency Inversion Principle (DIP)

In the realm of software development, writing code that is not only functional but also maintainable, scalable, and adaptable is crucial. One set of principles that aims to guide developers in achieving these goals is the S.O.L.I.D principles. These principles, coined by Robert C. Martin, provide a framework for writing clean, modular, and robust code. In this blog post, we'll embark on an introductory journey into the world of S.O.L.I.D principles, focusing specifically on the Single Responsibility Principle (SRP), along with a detailed example to elucidate its significance.

What are S.O.L.I.D Principles?

S.O.L.I.D is an acronym that stands for:

These principles serve as guidelines for writing maintainable and scalable object-oriented software. Each principle addresses a specific aspect of software design, promoting modularity, flexibility, and extensibility.

In this post we will dive deeper into the Dependency Inversion Principle (DIP)

Dependency Inversion Principle (DIP) is one of the five SOLID principles of object-oriented design, proposed by Robert C. Martin. It emphasizes decoupling high-level modules from low-level modules, promoting abstraction and dependency on abstractions rather than concrete implementations.

What is Dependency Inversion Principle?

Dependency Inversion Principle states that:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.

  2. Abstractions should not depend on details. Details should depend on abstractions.

In simpler terms, DIP encourages designing software components in a way that high-level modules (which contain the more abstract, policy-related logic) do not rely directly on low-level modules (which contain more detailed implementation logic). Instead, both high-level and low-level modules should depend on abstractions, which allows for flexibility, maintainability, and easier testing.

Example of Dependency Inversion Principle

Let's illustrate DIP with a simple example in Java.

Suppose we have a Payment class in our Website. It contains card option for payment as of now. Card API will help Payment class to make Card Payment through the Card Payment Gateway.

In this design, Payment depends directly on Card API, violating the Dependency Inversion Principle. If we later decide to change the payment method from card to UPI or any other medium, we would need to modify Payment, which increases coupling and violates the principle of abstraction.

Faulty Design Problems

  1. High Coupling: The Payment is tightly coupled with the Card API, making it difficult to change or extend the payment mechanism without modifying Payment.

  2. Difficulty in Testing: Testing Payment becomes challenging because it relies directly on Card API, making it hard to substitute with a mock or stub during testing.

Solving the Problem with Dependency Inversion Principle

To adhere to DIP, we can introduce an abstraction layer between Payment and Card. We can define an interface PaymentProcessor, which Payment will depend on, rather than depending directly on the concrete implementation:

Now, Payment depends on the abstraction PaymentProcessor, adhering to the Dependency Inversion Principle. This design allows for easier extension and modification. If we want to change the payment mechanism, we can simply have to make changes in PaymentProcessor, without needing to modify Payment and its other methods.
This way Payment class just has to know the amount to be paid and other related stuff and not about how to actually make a payment. That part will be handled by the PaymentProcessor.

Now, Lets say we change our payment method to UPI:

The Payment class remains intact even after changing the payment method because it is not dependent on the payment method anymore.

Benefits of Using Dependency Inversion Principle

  1. Flexibility: DIP makes the system more flexible and easier to adapt to changes, as dependencies are decoupled.

  2. Maintainability: By adhering to DIP, code becomes more maintainable since changes to low-level modules don't affect high-level modules.

  3. Testability: DIP facilitates easier testing, as dependencies can be easily substituted with mocks or stubs during unit testing.

In conclusion, Dependency Inversion Principle is a valuable principle in object-oriented design, promoting decoupling, abstraction, and flexibility. By adhering to DIP, software systems become more robust, maintainable, and testable, leading to better overall quality.

Stay Tuned!!

Did you find this article valuable?

Support Darsh's Blog by becoming a sponsor. Any amount is appreciated!