Search Knowledge

© 2026 LIBREUNI PROJECT

Dependency Inversion Principle (DIP)

Dependency Inversion Principle (DIP)

The Dependency Inversion Principle is the final “D” in SOLID. It states:

  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.

The Hierarchy Problem

In traditional software development, high-level logic (e.g., Business Rules) often depends directly on low-level details (e.g., Database drivers, API clients). This makes the high-level logic fragile and hard to test.

Example: Notification System

The Violation (Python)

class EmailService:
    def send(self, message):
        print(f"Sending email: {message}")

class PasswordReset:
    def __init__(self):
        self.service = EmailService() # Hard dependency on low-level detail

    def reset(self):
        # logic...
        self.service.send("Your password was reset.")

Here, PasswordReset (High-level) depends on EmailService (Low-level). If we want to send an SMS instead, we must modify the PasswordReset class.

Applying DIP

We introduce an abstraction (Interface) between them.

from abc import ABC, abstractmethod

# The Abstraction
class MessageService(ABC):
    @abstractmethod
    def send(self, message):
        pass

# Low-level implementation
class EmailService(MessageService):
    def send(self, message):
        print(f"Email: {message}")

class SMSService(MessageService):
    def send(self, message):
        print(f"SMS: {message}")

# High-level module depends on the abstraction
class PasswordReset:
    def __init__(self, service: MessageService):
        self.service = service # Dependency Injection

    def reset(self):
        self.service.send("Reset link sent.")

Dependency Injection (DI) vs. DIP

While they are related, they are not the same:

  • DIP is the architectural principle (What is the dependency direction?).
  • DI is a technique for achieving DIP (How do we provide the dependency?).

DIP in Java with Spring-like patterns

In Java, we often use frameworks to handle the “Inversion of Control”.

public interface UserRepository {
    void save(User user);
}

public class UserService {
    private final UserRepository repo;

    // The service doesn't know WHO implements UserRepository
    public UserService(UserRepository repo) {
        this.repo = repo;
    }

    public void register(User user) {
        repo.save(user);
    }
}

Why “Inversion”?

It is called “Inversion” because it flips the traditional dependency graph.

  • Traditional: High-level → Low-level.
  • DIP: High-level → Abstraction ← Low-level. Both sides now “meet” at the abstraction.

Benefits

  • Flexibility: You can swap out a SQL database for a NoSQL one without touching business logic.
  • Testability: You can inject “Mock” dependencies into your high-level classes.
  • Maintainability: Low-level changes are isolated from the rest of the application.

Summary

The SOLID principles work together to create software that is easy to maintain and extend. By following the Dependency Inversion Principle, you ensure that your most valuable code (Business Logic) is protected from the volatility of external tools and frameworks.