This article will present the Dependency Inversion Principle (DIP). Along with the introduction of this topic, examples from MechLynx project will be used (https://github.com/CharlieGearsTech/MechLynx)
SOLID principles were introduced by Robert C. Martin around 2000. These principles are “guidelines”, they are not rules due that the application of these principles for certain software systems will not be feasible or recommended in some cases. The goal of these principles is to create reusable software modules that are able to tolerate changes over time and are easy to understand.
DIP indicates that you will achieve more flexibility in your system if all source code dependencies refers only to abstractions. This is a recommendation since no SW system can rely only by abstractions, and probably stable concrete dependencies will not hurt your design.
Stable elements are those that rarely change and SW systems can rely on their own implementation with these elements. In contrast, volatile elements are those that change actively and are undergoing frequent change, you cannot rely on volatile concrete elements since .
DIP expresses that concrete implementations are less volatile than abstractions, this is due some changes at concrete implementations does not require changes at their abstractions, but changes at the abstractions will always correspond to a change to the concrete implementation.
So for example, when source code “B” depends on volatile concrete implementations “C”. “B” is prompted to change within any change of “C”. This means that recompilation and redeployment efforts increase. In contrast, when source code “B” depends on stable abstraction “A” “B” is not chained to changes of “A” (that can be less probable than “C” changes). This means that recompilation and redeployment efforts are maintained at the minimum possible.
In the MechLynx SW system, SW View depends on the PROPCompiler, which depends on the Inference Kernel. Thereby, any change on Inference Kernel will affect SW View.
To avoid this, PROPCompiler dependency should be inverted by a new interface, which defines a Factory class that delivers instances of different types of Compilers. For example, if Inference Kernel changes implies the creation of a new type of Compiler, in this case Dogo Compiler, then this interface will create an instance of the new DogoCompiler type and those changes will not affect the compilation of SE View.
Notice the type of the arrow, black arrows indicate “uses” relationship and white arrows indicate “is a” relationship. In this case, SE View uses Compiler; Dogo Compiler and PROPCompiler are a Compiler.
The runtime dependency of SE View to Inference Kernel is still there, the one that is avoided with this implementation is the compilation dependency.
The conclusion is that DIP helps us to define layers of SW Components that are independent between each other by the use of abstraction.