Upturn your dependencies with the Dependency Inversion Principle
I thought this was going to be the simplest principle to explain but after looking at descriptions of it I found the language to be difficult to follow. It took me longer to understand and document than all the other principles put together. The regularly used quote from Martin Fowler states:
a. High-level modules should not depend on low-level modules. Both should depend on abstractions.
b. Abstractions should not depend on details. Details should depend on abstractions.
To me this quote doesn't immediately conjure a mental image of what DIP is, so let’s instead take a look at what the words mean. Firstly Dependency, let’s assume we have a CustomerController class and this directly creates an instance of a customer repository class to access the database. This is a direct dependency on a concrete class and should be avoided as it leads to tightly coupled classes which are generally considered bad as they are harder to maintain, test etc.
Note that in this diagram the CustomerController is the high-level module and CustomerRepository is the low-level repository.
To solve that we ensure the dependency for the CustomerRepository is on an interface (an abstraction) instead of a concrete class, so we have an ICustomerRepository (we covered this in the Open Closed Principle). At this point the CustomerRepository would normally own the ICustomerRepository, so if the Customer Repository was changed so would the interface.
At this point we’ve covered all of the first part of Martin Fowlers quote, so:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
…in our example becomes:
The Controller shouldn't depend on the Repository. They should both depend on an abstraction (interface)
Now for the second part of the quote, this is where the “Inversion” part of the DIP steps in. The inversion means that instead of the CustomerRepository owning the ICustomerRepository (above diagram), the CustomerController owns it (below diagram).
When we say the CustomerController “owns” ICustomerRepository we mean that if the CustomerController requires a change in the way it uses the repository it can change the ICustomerRepository and then the CustomerRepository class will be changed so that it still does what the interface requires it to do. In that sense the change is passed down to the CustomerRepository class instead of the other way round.
So to translate the second part of Martin Fowlers quote:
Abstractions should not depend on details. Details should depend on abstractions.
…in our example becomes:
ICustomerRepository should not depend on the implementation (CustomerRepository), the implementation should instead be driven by changes to ICustomerRepository
Now this doesn’t follow some of the other articles I’ve read on the Dependency Inversion Principle, some focus more on Dependency Injection and Inversion of Control, which are related but I don’t see them as the full story. If you disagree let me know!