DEV Community

Cover image for SOLID: Dependency Inversion Principle (DIP) in C#
Aditya
Aditya

Posted on

2 1 1 1 1

SOLID: Dependency Inversion Principle (DIP) in C#

Introduction

The Dependency Inversion Principle (DIP) is one of the foundational principles of object-oriented programming and software design, forming the "D" in the SOLID acronym. This principle emphasizes designing software modules to reduce coupling and improve flexibility, scalability, and maintainability.

Code dependency

In this article, we'll delve into the essence of DIP and understand its importance through practical examples in C#.

What Is the Dependency Inversion Principle?

The Dependency Inversion Principle 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.

Simply put, the principle encourages us to depend on interfaces or abstract classes rather than concrete implementations. This makes the system easier to modify and extend without impacting other parts of the code.

A Problem Without DIP ❌

Code bug

Consider an e-commerce application where we need to send notifications via email. Here's a naive implementation:

public class EmailNotification
{
    public void Send(string message)
    {
        Console.WriteLine($"Email sent: {message}");
    }
}

public class OrderProcessor
{
    private readonly EmailNotification _emailNotification;

    public OrderProcessor()
    {
        _emailNotification = new EmailNotification();
    }

    public void ProcessOrder()
    {
        // Order processing logic
        Console.WriteLine("Order processed.");

        // Send notification
        _emailNotification.Send("Your order has been processed.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Here, OrderProcessor is tightly coupled to EmailNotification. If we want to add support for SMS notifications, we'll have to modify the OrderProcessor class, violating the Open/Closed Principle (OCP).

Applying DIP to the Solution

Correct

To follow DIP, we'll introduce an abstraction (INotification) that both EmailNotification and SmsNotification can implement. The OrderProcessor will depend on the abstraction, not the concrete implementations.

Step 1: Define the Abstraction

public interface INotification
{
    void Send(string message);
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the Abstraction

public class EmailNotification : INotification
{
    public void Send(string message)
    {
        Console.WriteLine($"Email sent: {message}");
    }
}

public class SmsNotification : INotification
{
    public void Send(string message)
    {
        Console.WriteLine($"SMS sent: {message}");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Refactor OrderProcessor to Use the Abstraction

public class OrderProcessor
{
    private readonly INotification _notification;

    public OrderProcessor(INotification notification)
    {
        _notification = notification;
    }

    public void ProcessOrder()
    {
        // Order processing logic
        Console.WriteLine("Order processed.");

        // Send notification
        _notification.Send("Your order has been processed.");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Inject the Dependency

Using dependency injection, we can decide the notification mechanism at runtime.

class Program
{
    static void Main(string[] args)
    {
        // Use EmailNotification
        INotification emailNotification = new EmailNotification();
        var orderProcessor = new OrderProcessor(emailNotification);
        orderProcessor.ProcessOrder();

        // Switch to SmsNotification
        INotification smsNotification = new SmsNotification();
        var smsOrderProcessor = new OrderProcessor(smsNotification);
        smsOrderProcessor.ProcessOrder();
    }
}
Enter fullscreen mode Exit fullscreen mode

Benefits of Applying DIP

  • Reduced Coupling: OrderProcessor is no longer dependent on specific implementations (EmailNotification or SmsNotification).
  • Extensibility: Adding new notification types, such as push notifications, requires no changes to OrderProcessor.
  • Testability: The use of abstractions makes it easy to mock INotification in unit tests

Conclusion

The Dependency Inversion Principle promotes flexibility and adaptability in software design by emphasizing the use of abstractions. By adhering to DIP, we create systems that are more maintainable, scalable, and resilient to change.

Thank-you

Let connect on LinkedIn and checkout my GitHub repos:

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

AWS Industries LIVE!

AWS Industries LIVE! features AWS Partners discussing various topics related to their industry, their solutions, and how they can help customers.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️