Program to an interface, not an implementation. (Gang of Four in Design Patterns:
Elements of Reusable Object-Oriented Software)
Even if this isn’t the definition of the dependency inversion principle (DIP), it is what it practically is. “Uncle Bob” has another version of the same thing:
Depend on abstractions, not on concretions. (Robert C. Martin)
And here is the official definition of the DIP:
A) High level modules should not depend on low level modules. Both should depend on abstractions.
B) Abstractions should not depend upon details. Details should depend upon abstractions. (Robert C. Martin)
These are quotes that sound nice but don’t say a much if we hear or read them first time without explanation. I will try to explain what DIP is and what makes it useful. We will find that these quotes all relate to each other.
Example of DIP
This is how we usually code: we use a lower level module straight from a higher level module. Wikipedia has a good diagram that visualizes this:
As we can see, higher level module Policy Layer depends on lower level Mechanism Layer. Mechanism Layer depends similarly on lower Utility Layer. This violates “A” from DIP’s definition (“High-level modules should not depend on low level-modules”). Policy Layer also depends on concretion. It depends on how Mechanism Layer is implemented. If Mechanism Layer is changed, it can affect Policy Layer or even force it to change. Thus it violates also “depend on abstractions, not on concretions”.
To make high-level modules to depend on abstraction, we shouldn’t think “What class or method we should call?”. We should rather think “What do we need? What kind of methods we need to call?”. Interfaces (abstractions) are the answer: that way we can define what high-level module really needs. Again, Wikipedia demonstrates this nicely through this diagram:
Now high-level modules (Policy and Mechanism Layer) have defined by themselves what they need (interface) and they depend on abstraction, not on concretion.
Now we have modules that apply DIP:
A) High-level module (Policy Layer / Mechanism Layer) does not depend on low-level module (Mechanism Layer / Utility Layer). Both depend on abstraction (Policy Service Interface / Mechanism Service Interface).
B) Abstraction (Policy Service Interface / Mechanism Service Interface) is not depending on details. Detail (Mechanism Layer / Utility Layer) depend on abstraction.
Explanation of The Name “Dependency Inversion”
Why this principle has the name “dependency inversion”? The reason is that the low-level module turns its dependency somehow the other way round. The high-level module used to depend on the low-level module, but now the low-level module has a dependency on (higher) abstraction. Low-level module’s dependency has gone “upside down” and there comes the name “dependency inversion”.
Don’t Apply DIP Everywhere
This principle should not be applied blindly for every class or every module. (OODesing.com)
DIP could be used almost everywhere like Robert C. Martin says “dependency Inversion can be applied wherever one class sends a message to another.” We have to carefully think when to use it.
Layering Makes Unit Testing Problematic
If we have layering like “Controller -> Business Layer -> Database Layer -> Database” with concrete classes, it is difficult to unit test (just) the controller. The reason is that the controller has the chained dependency on the database layer and further to the database. But if there are interfaces between layers, it is possible to unit test because we can use fake classes or mocks that implement interfaces.
Relationship with Open/Closed and Liskov Substitution Principles
If you consequently apply the Open/Closed Principle and the Liskov Substitution Principle to your code, it will also follow the Dependency Inversion Principle. (Thorben Janssen)
For a reminder I will put the former diagram here again:
Policy Layer applies OCP: we can expand it just by implementing different classes that implement Policy Service Interface. It is open for extensions without changes.
Mechanism Layer has to apply LSP or Policy Layer might not work: Mechanism Layer has to implement all methods in Policy Service Interface.
Somehow DIP was a little disappointment for me. One way it was just a combination of OCP and LSP. Maybe if I had written this series in opposite order, starting from the DIP, this probably would have been more interesting.
DIP taught me a concrete example what famous “program to an interface, not an implementation” practically means and what makes it a valuable tip. By applying DIP we can write loosely coupled code which is easier to unit test.
This was the last SOLID principle in this SOLID series but not the last blog post in this series: I will continue with one more blog post to this series, the conclusion.
Sources and Influences
- Dependency inversion principle (Wikipedia).
- Dependency Inversion Principle in OODesing.com.
- Encapsulation and SOLID by Mark Seemann in Pluralsight course.
- SOLID Design Principles Explained – Dependency Inversion Principle with Code Examples by Thorben Janssen in stackify.com.
- The answer to “What is the dependency inversion principle and why is it important?” by Derek Greer in stackoverflow.com.
- The Principles of OOD by Robert C. Martin in butunclebob.com.