# Dependency Inversion Principle (DIP) in Java

## Introduction

In software development, one of the biggest challenges is **tight coupling** — when one class directly depends on another concrete class. This makes systems hard to modify, test, and extend.

The **Dependency Inversion Principle (DIP)** helps solve this problem by introducing **abstractions** between components.

It is the **5th principle of SOLID** and is widely used in modern frameworks like **Spring**, **Hibernate**, and enterprise Java applications.

* * *

# What is Dependency Inversion Principle?

The **Dependency Inversion Principle** states:

> **High-level modules should not depend on low-level modules. Both should depend on abstractions.**  
> **Abstractions should not depend on details. Details should depend on abstractions.**

## 🧠 In Simple Words

👉 **Always depend on interfaces, not concrete classes.**

Instead of:

```plaintext
Notification → EmailService
```

Use:

```plaintext
Notification → MessageService ← EmailService
                                  SMSService
```

This makes your system flexible and maintainable.

* * *

# Real-World Analogy

Imagine a **mobile charger system**

*   Mobile = High-level module
    
*   Charger = Low-level module
    
*   USB Port = Abstraction
    

Your mobile doesn't depend on:

*   Samsung Charger
    
*   Xiaomi Charger
    
*   OnePlus Charger
    

It depends on **USB interface**.

That is **Dependency Inversion Principle**.

* * *

# Without Dependency Inversion (Bad Design)

Here the **high-level module depends directly on a low-level module**.

```java
// Low-Level Module
class EmailService {

    public void sendEmail(String message) {
        System.out.println("Sending Email: " + message);
    }
}

// High-Level Module
class Notification {

    private EmailService emailService;

    public Notification() {

        // Direct dependency (Tight Coupling)
        emailService = new EmailService();

    }

    public void notifyUser(String message) {

        emailService.sendEmail(message);

    }
}

// Main Class
public class Main {

    public static void main(String[] args) {

        Notification notification =
                new Notification();

        notification.notifyUser("Hello User!");

    }

}
```

* * *

## 🚨 Problems in This Design

Tight coupling between classes  
Cannot easily switch to SMS or WhatsApp  
Hard to test  
Violates Open/Closed Principle  
Difficult to extend

If tomorrow you add **SMS**, you must modify the `Notification` class.

That is bad design.

* * *

# Applying Dependency Inversion Principle (Good Design)

Now we introduce an **abstraction (interface)** between high-level and low-level modules.

* * *

# Step 1: Create Abstraction (Interface)

```java
// Abstraction
interface MessageService {

    void sendMessage(String message);

}
```

This interface defines **what should be done**, not **how**.

* * *

# Step 2: Implement Low-Level Modules

```java
// Low-Level Module 1
class EmailService implements MessageService {

    @Override
    public void sendMessage(String message) {

        System.out.println(
                "Sending Email: " + message
        );

    }

}

// Low-Level Module 2
class SMSService implements MessageService {

    @Override
    public void sendMessage(String message) {

        System.out.println(
                "Sending SMS: " + message
        );

    }

}
```

These classes provide **actual implementations**.

* * *

# Step 3: High-Level Module Depends on Interface

```java
// High-Level Module
class Notification {

    private MessageService messageService;

    // Constructor Injection
    public Notification(
            MessageService messageService
    ) {

        this.messageService = messageService;

    }

    public void notifyUser(String message) {

        messageService.sendMessage(message);

    }

}
```

Now `Notification` depends on:

```java
MessageService (Interface)
```

Not:

```java
EmailService (Concrete class)
```

* * *

# Step 4: Main Class (Runtime Dependency Injection)

```java
public class Main {

    public static void main(String[] args) {

        // Choose implementation
        MessageService service =
                new EmailService();

        Notification notification =
                new Notification(service);

        notification.notifyUser(
                "Dependency Inversion Applied!"
        );

    }

}
```

* * *

# 🔄 Execution Flow (Step-by-Step)

Let's understand what happens internally.

* * *

## Flow Explanation

1️⃣ Main class creates **EmailService**

```plaintext
EmailService service =
        new EmailService();
```

* * *

2️⃣ EmailService is passed to Notification

```plaintext
Notification notification =
        new Notification(service);
```

This is called:

👉 **Constructor Injection**

* * *

3️⃣ Notification stores the interface reference

```plaintext
private MessageService messageService;
```

* * *

4️⃣ When notifyUser() is called:

```plaintext
messageService.sendMessage(message);
```

Actual implementation runs:

```plaintext
EmailService.sendMessage()
```

* * *

# 🔁 Switching Implementation Easily

You can switch implementation without modifying `Notification`.

```java
MessageService service =
        new SMSService();

Notification notification =
        new Notification(service);

notification.notifyUser("Hello via SMS!");
```

No code changes required.

That is **flexibility**.

* * *

# Unit Testing Becomes Easy

You can create mock services:

```java
class MockMessageService
        implements MessageService {

    @Override
    public void sendMessage(String message) {

        System.out.println(
                "Mock Message Sent"
        );

    }

}
```

Now testing becomes:

✔ Simple  
  
✔ Fast  
  
✔ Independent

* * *

# 🧠 Core Concept Summary

### Without DIP

```plaintext
Notification → EmailService
```

Tight coupling ❌

* * *

### With DIP

```plaintext
Notification → MessageService ← EmailService
                                 SMSService
```

Loose coupling ✅

* * *

# Benefits of Dependency Inversion Principle

✔ Reduces tight coupling  
  
✔ Improves flexibility  
  
✔ Makes testing easier  
  
✔ Supports scalability  
  
✔ Encourages modular design  
  
✔ Improves maintainability  
  
✔ Enables Dependency Injection

* * *

# When NOT to Use DIP

Avoid DIP when:

Writing very small programs  
  
Simple scripts  
  
No expected extension  
  
Over-engineering risk

Use DIP when:

✔ Building scalable systems  
  
✔ Enterprise applications  
  
✔ Framework-level code  
  
✔ Systems with multiple implementations

* * *

# Complete Final Code (Clean Version)

```java
// Abstraction
interface MessageService {

    void sendMessage(String message);

}

// Low-Level Module
class EmailService
        implements MessageService {

    public void sendMessage(String message) {

        System.out.println(
                "Email: " + message
        );

    }

}

// Another Low-Level Module
class SMSService
        implements MessageService {

    public void sendMessage(String message) {

        System.out.println(
                "SMS: " + message
        );

    }

}

// High-Level Module
class Notification {

    private MessageService messageService;

    public Notification(
            MessageService messageService
    ) {

        this.messageService = messageService;

    }

    public void notifyUser(String message) {

        messageService.sendMessage(message);

    }

}

// Main Class
public class Main {

    public static void main(String[] args) {

        MessageService service =
                new EmailService();

        Notification notification =
                new Notification(service);

        notification.notifyUser(
                "DIP Applied Successfully!"
        );

    }

}
```

* * *

# 🧠 Final Thought

> **Dependency Inversion Principle transforms rigid systems into flexible architectures by making components depend on contracts instead of implementations.**

* * *
