Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) is the 'D' in the SOLID principles of object-oriented programming and design.
The Dependency Inversion Principle states:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
To put it more simply, DIP is about removing hard links between high-level and low-level objects so that they can be managed independently of each other. This is usually achieved by introducing an abstraction layer between the high-level and low-level modules.
Let's illustrate this with an example. Suppose we have an application for reading books, and there's a high-level BookReader
class that depends on a low-level PDFBook
class.
class PDFBook {
String getText() {
return "Contents of the PDF book.";
}
}
class BookReader {
PDFBook book;
BookReader(PDFBook aBook) {
this.book = aBook;
}
void read() {
System.out.println(book.getText());
}
}
This design violates the DIP because the BookReader
(a high-level module) is directly dependent on PDFBook
(a low-level module). If you wanted to read another book format, like EpubBook
, you would need to modify BookReader
.
By introducing an interface (abstraction), like ReadableBook
, both PDFBook
and EpubBook
can implement it:
interface ReadableBook {
String getText();
}
class PDFBook implements ReadableBook {
String getText() {
return "Contents of the PDF book.";
}
}
class EpubBook implements ReadableBook {
String getText() {
return "Contents of the Epub book.";
}
}
class BookReader {
ReadableBook book;
BookReader(ReadableBook aBook) {
this.book = aBook;
}
void read() {
System.out.println(book.getText());
}
}
In this design, BookReader
depends on the abstraction (ReadableBook
), and not on the low-level classes. Therefore, it follows the Dependency Inversion Principle. You can now easily read any book that implements ReadableBook
without any changes to BookReader
.
By following DIP, you make high-level modules independent from low-level modules, increasing the flexibility, decoupling, and reusability in your code.