SOLID - Dependency Inversion Principle

Solid Principles

← Prev

In this tutorial we are going to learn about the Dependency Inversion Principle (DIP).

Table of contents

GitHub repository

Reference

  • S Single Responsibility Principle
  • O Open-Closed Principle
  • L Liskov Substitution Principle
  • I Interface Segregation Principle
  • D Dependency Inversion Principle

Definition

Dependency Inversion Principle (DIP) states that high level classes must not depend on low level classes. Both must depend on abstractions.

The objective of this principle is to decouple the classes.

Low level classes

These are the classes that implements low level operations like access files, interacting with database, making network call, etc.

High level classes

These are the classes that contains some sort of business logic and uses the low level classes to carry out their tasks.

Example

In the following Java program we have the MySQL class which is the low-level class and it is used by the User class which is the high-level class.

class MySQL {
    public String connection() {
        return "Connection successful.";
    }

    public String insert() {
        return "Row inserted!";
    }
}


class PostgreSQL {
    public String dbCon() {
        return "Connected to database on port 5432";
    }

    public String create() {
        return "New row created!";
    }
}


class User {
    private final MySQL mySQL;

    public User(MySQL mySQL) {
        this.mySQL = mySQL;
    }

    public String createUser() {
        // do something...
        return mySQL.insert();
    }
}

Test

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class DIPTests {
    @Test
    @DisplayName("Using MySQL")
    public void shouldUseConcreteClass() {
        MySQL mySQL = new MySQL();
        User user = new User(mySQL);
        Assertions.assertEquals(user.createUser(), "Row inserted!");
    }
}

Problem

The high-level class User is directly dependent on the low-level class MySQL. If we want to change the database from MySQL to PostgreSQL then we would have to make changes in the User class which would clearly violet the Open-Closed Principle.

Solution

To solve this problem we would have to make the high-level class User depend on an abstraction. For this we are going to create the Database interface which is implemented by the MySQL and PostgreSQL database.

interface Database {
    public String connect();
    public String insert();
}


class MySQL implements Database {

    @Override
    public String connect() {
        return "Connected on port 3306.";
    }

    @Override
    public String insert() {
        return "Inserted 1 row.";
    }
}


class PostgreSQL implements Database {

    @Override
    public String connect() {
        return "Connected on port 5432.";
    }

    @Override
    public String insert() {
        return "Inserted 1 row.";
    }
}


class User {
    private final Database database;

    public User(Database database) {
        this.database = database;
    }

    public String create() {
        return database.insert();
    }
}

Test

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class DIPTests {
    @Test
    @DisplayName("Using abstraction")
    public void shouldUseAbstraction() {
        Database database = new MySQL();
        User user = new User(database);
        Assertions.assertEquals(user.create(), "Inserted 1 row.");
    }
}
← Prev