The last principle in SOLID is the Dependency Inversion Principle (DIP) stresses out the importance of having dependencies based on abstraction. It states that:
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.
The first statement of the principle simply states that classes should depend on interfaces or abstract classes instead of just another class. For the second statement, it is also important to not let abstraction depend on the details because whenever the details change, the abstraction will change and it will affect its dependencies.
By following the first part of the DIP we would have an extensible code. It would be easy to extend the functionality of our application since we only need to implement interfaces or extend abstract classes. We also allow the modules to be substitutable hence preventing us from violating the Liskov Substitution Principle. If we apply the second part of the DIP we can avoid the need to constantly modify existing interfaces and abstract classes along with its dependencies which also prevents us from violating the Open-Closed Principle.
Here is a sample that violates the DIP:
This code will work as it will eventually send an email as we expect it to once all the logic is there. However, what if the recipient of the email, in this case, the owner of the restaurant wants to receive an SMS notification regarding his sales report for the day. How are we going to do it?
By following the code structure above what could happen is that we will have another class let’s call it SmsNotificationService that we will also create an instance of it in the ReportsManager so that we can invoke the method. We will do that because there is no layer of abstraction in current code; we are unable to substitute which notification service to use. We also need to modify the ReportsManager class so that we can call on the method of the new SMS service. In that structure, we are violating multiple SOLID principles.
Here is the sample code where we apply the DIP. Now that we have an interface we are able to implement different notification services and we can easily create a new notification service let’s say a PushNotificationService without needing to worry about modifying our existing code. The ReportsManager class now asks which notification service to send the report to and it will accept any existing class that implements the INotificationService. It looks like a small change to the code but we can greatly see the effect on how we allow ourselves not to violate the other SOLID principles anymore.
To conclude, by putting the SOLID principles into practice, we are confident that our code will be maintainable, extensible, durable and testable. We also saw in this series how one principle is similar to another and how violating one principle can lead to violating the other principles; how applying one principle, implicitly makes us apply the other principles.
By knowing, understanding and applying the SOLID principles, we take a huge step in becoming a better programmer. Happy coding!