You are currently viewing Dependency Inversion Principle In Java

Dependency Inversion Principle In Java

Introduction

The Dependency Inversion Principle (DIP) is the “D” in SOLID and plays a crucial role in creating loosely coupled and testable applications.

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

And: Abstractions should not depend on details. Details should depend on abstractions.

In simple terms, instead of directly instantiating concrete classes, we should program against interfaces or abstract classes, allowing flexibility and separation of concerns.


1. Violating the Principle

Here’s a basic service that tightly couples the application logic to a specific database implementation:

class MySQLDatabase {
    void connect() {
        System.out.println("Connecting to MySQL...");
    }
}

class ReportService {
    private MySQLDatabase db = new MySQLDatabase();

    void generateReport() {
        db.connect();
        System.out.println("Generating report...");
    }
}

❌ Why this is bad:

  • Hard to replace MySQL with another database.
  • Hard to unit test ReportService in isolation.
  • Violates the Open/Closed Principle as well.

2. Applying the Dependency Inversion Principle

βœ… Step 1: Define an abstraction

interface Database {
    void connect();
}

βœ… Step 2: Implement it

class MySQLDatabase implements Database {
    public void connect() {
        System.out.println("Connecting to MySQL...");
    }
}

βœ… Step 3: Inject the abstraction into the service

class ReportService {
    private final Database db;

    public ReportService(Database db) {
        this.db = db;
    }

    public void generateReport() {
        db.connect();
        System.out.println("Generating report...");
    }
}

Now you can easily pass in a mock database for testing, or switch to another implementation like PostgreSQL.


3. Real-World Use Case: Notification System

Imagine you’re building an alert system that notifies users about system events. Initially, you’re asked to support email notifications only:

class AlertService {
    private final EmailNotifier notifier = new EmailNotifier();

    public void triggerAlert(String message) {
        notifier.send(message);
    }
}

class EmailNotifier {
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

❌ What’s wrong?

  • AlertService is tightly coupled to EmailNotifier
  • You can’t reuse AlertService with other notifiers (e.g., SMS, push)
  • Difficult to mock or replace in tests
  • Violates the Dependency Inversion Principle

βœ… Refactor Using DIP

We introduce a Notifier interface to invert the dependency:

interface Notifier {
    void send(String message);
}

Concrete implementations:

class EmailNotifier implements Notifier {
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

class SMSNotifier implements Notifier {
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

Refactored AlertService:

class AlertService {
    private final Notifier notifier;

    public AlertService(Notifier notifier) {
        this.notifier = notifier;
    }

    public void triggerAlert(String message) {
        notifier.send(message);
    }
}

βœ… Benefits:

  • Easily switch between notifiers
  • Use mock implementations for tests
  • AlertService depends on abstraction, not a specific channel

4. Benefits of Dependency Inversion

  • Promotes dependency injection
  • Improves unit testability
  • Makes the system flexible and modular
  • Encourages programming to interfaces over implementations

Conclusion

By following the Dependency Inversion Principle, your Java applications become more maintainable, scalable, and easier to test. It’s a foundational principle behind many modern patterns, including Hexagonal Architecture, Clean Architecture, and Dependency Injection Frameworks like Spring.

You can find the complete code of this article here in GitHub.

πŸ“š Related: Interface Segregation Principle

Noel Kamphoa

Experienced software engineer with expertise in Telecom, Payroll, and Banking. Now Senior Software Engineer at Societe Generale Paris.