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 moduleB
if when the program runs there is transfer of control from the code inA
to that inB
, typically via a function call.A moduleA
has a source code dependency or compile time dependency on moduleB
when the source code ofA
directly references the source code ofB
.
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)