Skip to main content

Section 10.8 Dependency Inversion

Related to all these ideas of interfaces and decoupling is another important idea, called dependency inversion. In essence this concept is about two different dependencies:
A module (think class/file) A has a runtime dependency on module B if when the program runs there is transfer of control from the code in A to that in B, typically via a function call.
A module A has a source code dependency or compile time dependency on module B when the source code of A directly references the source code of B.
As an example of these concepts, consider the GradeReader and Summary classes. When the program executes by running Main, an instance of the Summary class is created, and it is given an instance of the GradeReader class as a parameter. It then calls that class’ hasNext() and next() methods (or the original hasMoreEntries() and processNextGradeRow() names). At that point in time the code execution moves from Summary to GradeReader via the function call, resulting in a runtime dependency from Summary to GradeReader.
On the other hand, there is no source code dependency between the two classes. Instead they both depend on the Iterator interface. Before we had that interface, the Summary class had a parameter of type GradeReader and there was a source code dependency from Summary to GradeReader. The use of the interface allowed us to break that source code dependency. This is dependency inversion:
Dependency Inversion occurs when the source code dependency between two modules opposes their runtime dependency. This is done by the use of an interface on which both modules depend.
Dependency inversion is essential for the health of a software, because it allows us to independently develop and test the two modules.
A deeper reason has to do with the so-called Dependency Inversion Principle, that we will look at more closely at a later time. (TODO: Dependency Inversion Principle link)