DIP (dependency inversion principle)

img

Dependency Inversion Principle (DIP)

The Dependency Inversion Principle (DIP) is one of the five SOLID principles of object-oriented design. It states that high-level modules should not depend on low-level modules. Both should depend on abstractions (e.g., interfaces). Additionally, abstractions should not depend on details. Details (concrete implementations) should depend on abstractions (eg. interfaces).

Key Benefits:

  1. Decoupling: Reduces tight coupling between high-level and low-level modules, making the system more modular.
  2. Flexibility: Easier to swap out implementations without affecting high-level modules.
  3. Maintainability: Simplifies changes and enhancements, as changes in low-level modules do not ripple through the entire system.

Example:

Consider a scenario where a CustomerService class depends on a MySQLDatabase class for data storage.

Violation of DIP:

public class MySQLDatabase {
    public void saveData(String data) {
        // Code to save data to MySQL database
    }
}

public class CustomerService {
    private MySQLDatabase database = new MySQLDatabase();

    public void addCustomer(String customerData) {
        database.saveData(customerData);
    }
}

In this case, CustomerService is tightly coupled to MySQLDatabase. If you want to switch to a different database, you must modify CustomerService.

Adhering to DIP:

  1. Define an Abstraction:

    public interface Database {
        void saveData(String data);
    }
    
  2. Implement Concrete Classes:

    public class MySQLDatabase implements Database {
        @Override
        public void saveData(String data) {
            // Code to save data to MySQL database
        }
    }
    
    public class MongoDBDatabase implements Database {
        @Override
        public void saveData(String data) {
            // Code to save data to MongoDB
        }
    }
    
  3. Refactor High-Level Module:

    public class CustomerService {
        private Database database;
    
        public CustomerService(Database database) {
            this.database = database;
        }
    
        public void addCustomer(String customerData) {
            database.saveData(customerData);
        }
    }
    
  4. Usage:

    public class Main {
        public static void main(String[] args) {
            Database mySQLDatabase = new MySQLDatabase();
            CustomerService customerService = new CustomerService(mySQLDatabase);
            customerService.addCustomer("John Doe");
    
            // Switch to MongoDB without changing CustomerService
            Database mongoDBDatabase = new MongoDBDatabase();
            customerService = new CustomerService(mongoDBDatabase);
            customerService.addCustomer("Jane Doe");
        }
    }
    

Summary:

The Dependency Inversion Principle promotes the use of abstractions to decouple high-level modules from low-level implementations. By relying on interfaces or abstract classes, you can swap out concrete implementations with minimal impact on the system, enhancing flexibility, maintainability, and modularity.

To follow this well you should be using interfaces.

You should start by creating an interface and writing out how you want the interface to look like without being bogged down by implementation details yet.

Even when it seems like the interface is not needed yet and there will be just one implementation, creating an interface switches our mind into a different mindset.

Creating an interface forces us to scope down what the object is allowed to do.

Creating an interfaces makes it easy to see what is the object allowed to do.

Creating an interface makes it more likely that some javadoc documentation is written.

Further notes

Also note: Sometimes POJO is all you need.

There is some cost to creation of the interface. Not just in a manner of creating initially (hint: lower this cost by using intellij extract interface functionality to speed up creation of interfaces from classes that already exist). But more so later to hop from interface down to implementation.

Hence, at times it makes sense just to create a data object holding data without making an interface to it for the sake of Keep it super Simple (KISS).

Relationships


Backlinks