Skip to main content

Dependency Inversion Principle (DIP)

 The Dependency Inversion Principle can be stated as follows:

  1. High-level classes should not depend on low-level classes. Both of them should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend upon abstractions.
The first part consists of dependency between high-level and low-level classes. A high-level class is a class that does something significant for the application, while a low-level class does some auxiliary work. The secondary part tells us that abstraction should not depend on details, the details should depend on abstractions.

Let's suppose you build an authentication system that needs to manage users. Where a way to change password is required. When a password is change a notification is sent to the user. In this case the class in charge on doing the user management is a high level class and the class sending the notification is a low-level class.

Consider the classes shown below:


The  high-level class, UserManager contains the changePassword() method. The UserManager class depends on the EmailNotifier class for sending emails notification to the user. In this case UserManager creates an instance of EmailNotifier.

public void ChangePassword() { 
     EmailNotifier notifier = new EmailNotifier();
     Notify("Password was changed"); 

You can see the changePassword() method instantiates EmailNotifier and then calls its Notify() method. This design seems quite normal but, there is a problem with it. UserManager has too much dependency on EmailNotifuer. EveryTimeEmailNotifierChanges, UserManager might need some correction. Also alterations to the notification system may require a modification in the UserManager class. For example, providing SMS notification.

To solve this problem, Dependency Inversion Principle suggest depend on abstraction. As shown in the diagram below:



Now the UserManager class no longer uses EmailNotifier directly, instead the interface INotifier has been introduced. And the code should look something like this:

Let's start with the INotifier interface that consist of a simple method called notify().

public interface INotifier {
    void notify(String message);
}


Then we add two classes EmailNotifier and SMSNotifier that would implement the interface INotifier.

public class SMSNotifier implements INotifier{

    @Override
    public void Notify(String message) {
        System.out.println(message);
    }
    
}

public class EmailNotifier implements INotifier{

    @Override
    public void Notify(String message) {
        System.out.println(message);
    }
    
}

And last but not least, add the UserManager class.

public class UserManager {
    
    public INotifier notifier;

    public UserManager(INotifier notifier) {
        this.notifier = notifier;
    }

    public INotifier getNotifier() {
        return notifier;
    }

    public void setNotifier(INotifier notifier) {
        this.notifier = notifier;
    }
    
    public void ChangePassword(String username, String oldPass, String newPass){
        this.notifier.Notify("Password was changed");
    }
}

See that the UserManager class defines a public property notifier of type INotifier. And also has a constructor that accepts a parameter of type INotifier. The method changePassword() is supposed to change the password and to call the Notify() method. 

public static void main(String[] args) {
     Scanner sc = new Scanner(System.in);
     INotifier notifier = null;
     String notificationType = sc.nextLine();
     switch (notificationType) {
         case "email":
             notifier = new EmailNotifier();
             break;
         case "sms":
             notifier = new EmailNotifier();
             break;
     }
        
     UserManager userMgr = new UserManager(notifier);
     userMgr.ChangePassword("user", "oldpass", "newpass");
}


You can see an instance an instance of UserManager is created by passing this notifier object in the constructor, then we call the changePassword() method specifying a username and password(values thet were not used). Finally we can see the message "Password was changed".

Reference

Joshi, B. (2016b). Beginning SOLID Principles and Design Patterns for ASP.NET Developers (English Edition) (1st ed.). Apress.


Comments