You are here

SOLID Principles : The Dependency Inversion Principle

SOLID Principles : The Dependency Inversion PrincipleAlmost everywhere when we talk about delivery of a product, the first step comes in mind is it’s design. The more focus we put into the design, the better the product will look. Every design has some design principles that need to be followed while designing a product.  Hence, design principles have a crucial role in any product delivery. Design Principles help teams with decision making. In this article, We will discuss about ‘SOLID Principles : The Dependency Inversion Principle’.

A few simple principles or valuable questions can guide our team towards taking relevant decisions. SOLID Principles are the set of five principles used to design a software. In fact, the word ‘SOLID’ is the acronym for the set of five principles that contains the first letter of each principle. Let’s discuss SOLID Principles : The Dependency Inversion Principle in detail and related concepts.

What is Dependency Inversion Principle (DIP) ?

The Dependency Inversion Principle (DIP) states that high-level modules should not depend upon low-level modules; they should depend on abstractions. Secondly, abstractions should not depend upon details; details should depend upon abstractions.

This way, instead of high-level modules depending on low-level modules, both will depend on abstractions. Every dependency in the design should target an interface or an abstract class. No dependency should target a concrete class.

The idea is that we isolate our class behind a boundary formed by the abstractions it depends on. If all the details behind those abstractions change, then our class is still safe. This helps keep coupling low and makes our design easier to change. DIP also offers us to test things in isolation, details like database are plugins to our system.

Robert Martin equated the Dependency Inversion Principle, as a first-class combination of the Open Closed Principle and the Liskov Substitution Principle.

Example: Code that violates Dependency Inversion Principle

Suppose a book store asked us to build a new feature that enables customers to put their favorite books on a shelf.

In order to implement the new functionality, we create a lower-level Book class and a higher-level Shelf class. The Book class will allow users to see reviews and read a sample of each book they store on their shelves. The Shelf class will let them add a book to their shelf and customize the shelf. For example, observe the below code.

public class Book {

    void seeReviews() {
         ...
    }

    void readSample() {
         ...
    }
}


public class Shelf {

     Book book;

     void addBook(Book book) {
          ...
     }

     void customizeShelf() {
          ...
     }
}

Everything looks fine, but as the high-level Shelf class depends on the low-level Book, the above code violates the Dependency Inversion Principle. This becomes clear when the store asks us to enable customers to add DVDs to their shelves, too. In order to fulfil the demand, we create a new DVD class:

public class DVD {

     void seeReviews() {
          ...
     }

     void watchSample() {
          ...
     }
}

Now, we should modify the Shelf class so that it can accept DVDs, too. However, this would clearly break the Open/Closed Principle too.

Example: Code that follows Dependency Inversion Principle

The solution is to create an abstraction layer for the lower-level classes (Book and DVD). We’ll do so by introducing the Product interface, both classes will implement it. For example, below code demonstrates the concept.

public interface Product {

    void seeReviews();

    void getSample();

}

public class Book implements Product {

    @Override
    public void seeReviews() { 
          ...
    }

    @Override
    public void getSample() {
          ...
    }
}

public class DVD implements Product {

    @Override
    public void seeReviews() { 
         ...
    }

    @Override  
    public void getSample() {
          ...
    }
}

Now, Shelf can reference the Product interface instead of its implementations (Book and DVD). The refactored code also allows us to later introduce new product types (for instance, Magazine) that customers can put on their shelves, too.

public class Shelf {

    Product product;

    void addProduct(Product product) {
          ...
    }

    void customizeShelf() {
          ...
    }
}


The above code also follows the Liskov Substitution Principle, as the Product type can be substituted with both of its subtypes (Book and DVD) without breaking the program. At the same time, we have also implemented the Dependency Inversion Principle, as in the refactored code, high-level classes don’t depend on low-level classes, either. Let’s check with the class diagram in both the cases.

 

Interface Segregation Principle Violation

 

SOLID Principles : The Interface Segregation Principle

Other Example

Here is another example : A program depends on Reader and Writer interfaces that are abstractions, and Keyboard and Printer are details that depend on those abstractions by implementing those interfaces. Here CharCopier is oblivious to the low-level details of Reader and Writer implementations and thus you can pass in any Device that implements the Reader and Writer interface and CharCopier would still work correctly.

public interface Reader {

    char getChar();
}

public interface Writer {

    void putChar(char c);
}

public class CharCopier {

     void copy(Reader reader, Writer writer) {

     int c;

     while ((c = reader.getChar()) != EOF) {

          writer.putChar();

     }
  }
}


public Keyboard implements Reader{
       ...
}

public Printer implements Writer{ 
       ...
}


How is DIP related to Dependency Injection of Spring Framework?

It would be correct if you think that Dependency Inversion Principle is related to Dependency Injection as it applies to the Spring Framework. Uncle Bob Martin introduced the concept of Dependency Inversion before Martin Fowler introduced the term Dependency Injection. These both concepts are extremely related. Dependency Inversion is more concentrated on the structure of your code. Moreover, its focus is keeping your code loosely coupled. On the other hand, Dependency Injection is about how the code functionally works.

Dependency Inversion Principle has been very well implemented in Spring framework, the beauty of this design principle is that any class which is injected by DI framework is easy to test with the mock object and easier to maintain because object creation code is centralized in the framework and client code is not messed up with that.

What is the benefit of Dependency Inversion Principle ?

Below are some of the benefits when we apply this principle in our code.

1) Keeps your code loosely coupled
2) Easier Maintenance
3) Better Code Reusability

That’s all about ‘SOLID Principles : The Dependency Inversion Principle’. Moreover, Wikipedia defines this Principle like this. In order to learn the most commonly used design principles, kindly visit the article on ‘OOPs Design Principles‘. Apart from ‘SOLID Principles : The The Dependency Inversion Principle’, we have discussed other principles as separate articles respectively.

Links to Other Design Principles

Single Responsibility Principle

Open Closed Principle(OCP)

Liskov’s Substitution Principle(LSP)

Interface Segregation Principle(ISP)

close

One thought on “SOLID Principles : The Dependency Inversion Principle

  1. Thanks for writing such awesome articles on the SOLID Design Principle. I loved reading all of them. The definition is explained to the point in a very simple way. Code examples are good as well. Hat’s off to the writer @devs5003

Leave a Reply

Top