Dependency Inversion Principle

The Dependency Inversion Principle (DIP) is one of the SOLID principles. It concerns the relationship between modules or classes. It is summarised by Robert Martin in these two sentences:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.

These are pithy and, deceptively simple, statements. But what do they mean and how (and when) should we apply them?

High vs Low level modules

A Low level module is code which carries out some specific, concrete functionality. For example an HTTP library, a database adapter, or a vector graphics module. All provide specific low-level functionality.

A High Level module is code which models core application functionality. A high level module in a stock management application, for example, might manage updating stock levels (StockUpdater).

High Level depending on low level

In the example of a stock management application, a stock management update message in the StockUpdater might be stockUpdater.increaseBy(widget, 10). This will, ultimately lead to a call to a lower-level database adapter to update a table or tables in the database.

A naive and simplified implementation of this might look like:

class StockUpdater {private final PostgresAdapter adapter;public StockUpdater(PostgresAdapter adapter) {
this.adapter = adapter;
}
public void update(StockItem item, int quantity){
postgresAdapter.update(item, quantity);
}
}
public class PostgresAdapter {...public void update(StockItem item, int quantity) {
// Add item to database
}
}

The issue here is that the StockUpdater Class is being dictated to by the interface of the PostgresAdapter. The question to ask is: why should a StockUpdater know about the type of underlying datastore? Should the functionality of the StockUpdater need to change if the database was switched to MySQL or MongoDB? Clearly not. So it follows that the StockUpdater should interface with something (a) that it is in control of, and (b) something more generic than the PostgresAdapter.

The DIP says the solution is to use an Interface. That way, the StockUpdaterclass can define the interface it wants to use (thus inverting control), and the database adapter can then implement that interface.

In Java this would look like:

class StockUpdater {private final DataStore datastore;public StockUpdater(DataStore datastore) {
this.datastore = datastore;
}
public void update(StockItem item, int quantity){
datastore.update(item, quantity);
}
}
public interface DataStore {
update(StockItem item, int quanity)
}
public class PostgresAdapter implements DataStore {...public void update(StockItem item, int quantity) {
// Add item to database
}
}

In this simple example, the code changes are minimal. But the important thing is what it achieves. Now there is an interface that defines the relationship between the StockUpdater and the PostgresStore. The subtle, but key point, is that the DataStore interface is defined by the requirements of theStockUpdater not the PostgresAdapter. The PostgresAdapter doesn’t need any knowledge of StockUpdater, but just needs to implement the DataStore interface.

Have a good day!

    Harsha Vardhan Dharmavarapu

    Written by

    Product Engineer @ Go-Jek Tech | IITan | Ideologist | Finding peace on chaos.

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade