This week I wanted to write a post about the last SOLID design principle, Dependency Inversion. This principle deals with the relationship between interfaces that operate at a high system level and low system level interfaces.
Composed of two parts, the Dependency Inversion Principle as stated by Robert C. Martin is:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
While the name “Dependency Inversion” might imply inverting the dependency relationship between modules, applying this principle actually involves building both high-level and low-level modules from the same abstraction. Constructing your code to abide by the previous SOLID principles, particularly the Open/Closed Principle and the Liskov Substitution Principle, should implicitly also result in code that abides by the Dependency Inversion Principle. The Open/Closed Principle states that a software module should be open for extension, but closed for modification, and the Liskov Substitution Principle states that an interface should be able to be replaced by a separate implementation of the same parent interface without compromising the functioning of the software.
The author includes another coffee machine themed example, detailing how to apply the SOLID design principles to two separate classes of coffee machine. Starting with two classes, BasicCoffeeMachine and PremiumCoffeeMachine. The classes are very similar, with the only differences being an extra class variable representing a coffee bean grinder and a method to brew espresso for the PremiumCoffeeMachine class. The author describes their process for defining suitable abstractions for a piece of software representing coffee machines, including their decision to have the methods for brewing filter coffee and espresso split into two interfaces. A potential coffee machine class representing a machine with the capability to brew either filter or ground coffee can easily be built by implementing both of these interfaces and overriding the methods within the concrete classes.
Refactoring the code this way enables independence between interfaces and implementations and makes expanding on the code base easier and safer. With fewer dependencies between modules, the effects of any changes to existing code won’t ripple out to other parts of your software.
I wanted to review this principle specifically because I wanted to refactor the interfaces from some old projects of mine. I wrote some Java classes meant to represent characters in a fantasy role-playing game, complete with character levels and statistics. As I left it though, the code is only capable of creating characters meant for the user, and not any non-playable characters or any other sort of entities. I want to go back and redesign my code so that I have fewer methods that only function with specific classes, and more functionality to create and edit characters. I remember I had a lot of trouble trying to increment a character’s level by 1 and increasing their stats accordingly, and I get the feeling that refactoring my code with the SOLID principles in mind will help me get it to a place I am happier with.
From the blog CS@Worcester – Michael's Programming Blog by mikesprogrammingblog and used with permission of the author. All other rights reserved by the author.