Dependency Inversion Principle (DIP)

Introduction

In this article, we will cover the Dependency Inversion Principle (DIP).

Dependency Inversion Principle (DIP)

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend on abstractions.
  • This can be hard to understand at first, but if you've worked with .NET/.NET Core framework, you've seen an implementation of this principle in the form of Dependency Injection (DI). While they are not identical concepts, DIP keeps high-level modules from knowing the details of their low-level modules and setting them up. It can accomplish this through DI. A huge benefit of this is that it reduces the coupling between modules. Coupling is a very bad development pattern because it makes your code hard to refactor.

Let’s take an example of User Authentication.

Suppose we’re building a system that allows users to authenticate. Initially, users authenticate using a username and password system stored in a local database. However, you anticipate that in the future, we may wish to allow users to authenticate using third-party services like Google, Facebook, or even biometrics.

Violation of DI/Bad Code

using System;

namespace DesignPattern
{
    public class DatabaseAuthenticator
    {
        public bool Authenticate(string username, string password)
        {
            // Logic to authenticate using a database
            Console.WriteLine($"Authenticating {username} using database.");
            return true;
        }
    }

    public class AuthenticationService
    {
        private DatabaseAuthenticator _authenticator = new DatabaseAuthenticator();

        public bool AuthenticateUser(string username, string password)
        {
            return _authenticator.Authenticate(username, password);
        }
    }
}

Note. This design tightly couples the AuthenticationService to the DatabaseAuthenticator.

Using DI / Good Code

using System;

namespace DIPDemo
{
    //Define the Interface
    public interface IAuthenticator
    {
        bool Authenticate(string identifier, string credential);
    }

    //Concrete implementations
    public class DatabaseAuthenticator : IAuthenticator
    {
        public bool Authenticate(string username, string password)
        {
            // Logic to authenticate using a database
            Console.WriteLine($"Authenticating {username} using database.");
            return true;
        }
    }

    public class GoogleAuthenticator : IAuthenticator
    {
        public bool Authenticate(string token, string _)
        {
            // Logic to authenticate using Google
            Console.WriteLine($"Authenticating using Google with token: {token}");
            return true;
        }
    }

    //The AuthenticationService class will now depend on the abstraction
    public class AuthenticationService
    {
        private readonly IAuthenticator _authenticator;

        public AuthenticationService(IAuthenticator authenticator)
        {
            _authenticator = authenticator;
        }

        public bool AuthenticateUser(string identifier, string credential)
        {
            return _authenticator.Authenticate(identifier, credential);
        }
    }
    
    //Testing the Dependency Inversion Principle 
    public class Program
    {
        public static void Main()
        {
            var dbAuthenticator = new DatabaseAuthenticator();
            var authServiceWithDB = new AuthenticationService(dbAuthenticator);
            authServiceWithDB.AuthenticateUser("vyelve", "password123$");

            var googleAuthenticator = new GoogleAuthenticator();
            var authServiceWithGoogle = new AuthenticationService(googleAuthenticator);
            authServiceWithGoogle.AuthenticateUser("OAuthTokenHere", "");
            
            Console.ReadKey();
        }
    }
}

DI

Note. Here, the AuthenticationService is decoupled from any specific authentication method. This allows for easy addition of new methods or changes to existing ones without affecting the central AuthenticationService.

Up Next
    Ebook Download
    View all
    Learn
    View all