Dependency Inversion Principle (DIP)
Tags: java, designpatterns, programming, tutorial
Published: 2023-11-04 16:58:37

In the Dependency Inversion Principle (DIP), the key idea is to let something else create objects for you, and you use those objects without having to create them directly. **_You "invert" the control of object creation._** 

Imagine it like this: Instead of going to the store to buy groceries (creating objects directly), you order groceries online (letting someone else create and deliver them) and use them as needed. 

In the same way, DIP encourages you to let a framework or other class create objects and inject (provide) them where you need them in your code. This makes your code more flexible and easier to change because you're not responsible for creating and managing everything yourself.

Let's explain DIP to a novice with an example where dependencies are injected manually via a configuration class:

**Example (Dependency Injection with Manual Configuration):**

Imagine you are building a simple notification system, and you want to send notifications via different channels such as email and SMS. Traditionally, you might create classes that directly use specific notification channels.

**Violation of DIP (Without Dependency Injection):**

```java
class EmailNotification {
    public void sendEmail(String to, String message) {
        // Code to send an email
    }
}

class SMSNotification {
    public void sendSMS(String to, String message) {
        // Code to send an SMS
    }
}
```

In this code, the `EmailNotification` and `SMSNotification` classes directly depend on specific notification channels, violating DIP because they tightly couple to low-level modules.

Now, let's adhere to DIP using manual dependency injection through a configuration class:

**Adhering to DIP (With Dependency Injection via Configuration):**

```java
interface NotificationService {
    void sendNotification(String to, String message);
}

class EmailNotification implements NotificationService {
    public void sendNotification(String to, String message) {
        // Code to send an email
    }
}

class SMSNotification implements NotificationService {
    public void sendNotification(String to, String message) {
        // Code to send an SMS
    }
}

class NotificationApp {
    private NotificationService notificationService;

    public NotificationApp(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void sendNotification(String to, String message) {
        notificationService.sendNotification(to, message);
    }
}
```

In this adhering-to-DIP example:

- We define an interface `NotificationService` that defines the common behavior for sending notifications.
- The `EmailNotification` and `SMSNotification` classes implement this interface, specifying how they send notifications.
- The `NotificationApp` class depends on the `NotificationService` interface and can work with any class that implements this interface.

**Configuration Class (Manual Dependency Injection):**

Now, let's manually configure the dependencies using a configuration class:

```java
public class AppConfig {
    public NotificationService emailNotificationService() {
        return new EmailNotification();
    }

    public NotificationService smsNotificationService() {
        return new SMSNotification();
    }

    public NotificationApp notificationApp() {
        return new NotificationApp(emailNotificationService());
    }
}
```

In this `AppConfig` class:

- We define two methods, `emailNotificationService` and `smsNotificationService`, which create instances of `EmailNotification` and `SMSNotification`.
- We also define a method `notificationApp` that creates an instance of `NotificationApp` and injects the `emailNotificationService` into it.

This manual configuration allows us to inject dependencies into the `NotificationApp` class without changing its code. We can switch notification services by modifying the `AppConfig` class while adhering to the Dependency Inversion Principle.

**Benefits of Manual Dependency Injection:**

- The `NotificationApp` class is no longer tightly coupled to specific notification channels, making it more flexible and maintainable.
- We can easily switch between different notification services by modifying the configuration class, promoting adherence to DIP.

In summary, the Dependency Inversion Principle suggests that high-level modules should not depend on low-level modules. Instead, they should both depend on abstractions. Manual dependency injection, as shown in this example, helps achieve this separation of concerns and promotes flexibility in your code.

> "Your feedback and ideas are invaluable – drop a comment, and let's make this even better!"

😍 If you enjoy the content, please 👍 like, 🔄 share, and 👣 follow for more updates!
Join me on a professional journey through my LinkedIn profile: [Linkedin Profile](https://www.linkedin.com/in/kurmivivek295/)