What is "D" in SOLID?

The Dependency Inversion Principle (DIP) is one of the five SOLID principles of object-oriented design and programming. It focuses on decoupling high-level and low-level modules through an abstraction layer. The principle consists of two key parts:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.

In simpler terms, DIP suggests that our code should depend on abstract interfaces or classes rather than concrete implementations. This approach leads to more modular, flexible, and maintainable code.

Example Violating DIP

Consider a simple application where a high-level module OrderProcessor directly depends on a low-level module MySQLDatabase to store order data. This direct dependency violates DIP.

class MySQLDatabase:
    def store(self, order):
        # Store the order in a MySQL database
        pass

class OrderProcessor:
    def __init__(self):
        self.database = MySQLDatabase()

    def process(self, order):
        # Process the order
        self.database.store(order)
        return "Order processed"

        

In this example, OrderProcessor is tightly coupled with MySQLDatabase. If we want to change the database to PostgreSQL or a mock database for testing, we need to modify the OrderProcessor class.

Refactored Example Adhering to DIP

To adhere to DIP, we introduce an abstract class or interface for the database operations and then implement this interface in our concrete database classes.

class DatabaseInterface:
    def store(self, order):
        raise NotImplementedError

class MySQLDatabase(DatabaseInterface):
    def store(self, order):
        # Store the order in a MySQL database
        pass

class PostgreSQLDatabase(DatabaseInterface):
    def store(self, order):
        # Store the order in a PostgreSQL database
        pass

class OrderProcessor:
    def __init__(self, database: DatabaseInterface):
        self.database = database

    def process(self, order):
        # Process the order
        self.database.store(order)
        return "Order processed"

        

In the refactored example:

  • DatabaseInterface is an abstraction that defines a method for storing data.
  • MySQLDatabase and PostgreSQLDatabase are concrete implementations of DatabaseInterface.
  • OrderProcessor depends on the abstraction (DatabaseInterface), not on the concrete MySQLDatabase or PostgreSQLDatabase. This allows us to easily switch databases without changing the OrderProcessor class.

By following DIP, the OrderProcessor becomes more flexible and easier to maintain. Changing or extending the data storage functionality doesn't require changes to the high-level module, as it relies on abstraction rather than concrete details.

To view or add a comment, sign in

More articles by Anuj Sharma

Explore content categories