Through the corroded dripping pipes of transitive dependencies, ripple effects gush. Programmers sometimes spend arduous days clambering down into the darkened building-tall labyrinths of plumbing for no other reason than to rip out some of those transitive dependencies so that predictability of cost of change might improve (amplification offering prime motivation). Those programmers that return often, after lengthy recuperation, babble of ancient principles glimpsed in new garb.
Consider the spoiklin
diagram of figure 1, in which three methods
c() - call a
d(), which in turn calls five others.
Figure 1: A simple system of nine methods.
How many transitive dependencies appear in figure 1? This essentially
asks how many ways we can fall from the top to the bottom
a() there is only one path,
d(), but from
d() five possible choices
a() can reach the bottom in five ways:
a() → d() → e()
a() → d() → f()
a() → d() → g()
a() → d() → h()
a() → d() → i()
Thus there are five transitive dependencies reaching
a() and the same amounts from
c(), giving a total of 3 x 5 = 15. In general this
configuration, where n elements depend ultimately on m
elements, generates n x m transitive dependencies.
Now consider that the programmer wishes to further keep de-couple the
three client methods from the implementation of the utility
d(). To do so, the programmer unsheathes the
inversion principle and slashes a new interface with an
d() method which a concrete class will then
implement. Figure 2 shows the resulting system.
Figure 2: Dependency inversion.
In figure 2, the same three methods
c() - call
d() below but now via the new
interface. The concrete class implementing the utility method also has
d(), one on which no dependencies fall (clients invoke
the interface method, not the concrete method) so that the
d() appears on the top row, a dashed-line
showing its inheritance dependency on its corresponding interface
How many transitive dependencies does figure 2 contain? All four
methods on top terminate on the abstract
yielding four transitive dependencies. Concrete method
also has five dependencies on the original bottom-row methods, bringing the total number of transitive dependencies to nine.
Note, however, that a fundamental structural change has taken place. The number of transitive dependencies can be now expressed as a sum rather than a product. That is, the original system generated 3 x 5 transitive dependencies; the new system generates 3 + 6 transitive dependencies. In general we can say that where n original elements depend ultimately on m original elements, the above dependency inversion will reduce the number of transitive dependencies from n x m to n + m + 1. If n and m are large, this can be a significant saving.
Just as mathematical logarithms help ease calculation burden by reducing a product to an addition, so too can the dependency inversion principle help reduce the number of transitive dependencies from a product to an addition. Furthermore, this application of the dependency inversion principle has also reduced the system's depth, "depth" being the average length of the system's transitive dependencies. In figure 1, all the transitive dependencies are of length three; in figure 2, they are all two elements long. Given that ripple effects cause most destruction when wormed deep in a vast profusions of transitive dependencies, the dependency inversion principle would seem a double benefactor.
Old crotchety principles sometimes surprise. The dependency inversion principle has long earned respect from programmers for its prowess at smashing the rigidity and fragility of otherwise un-lubricated systems. Yet this fails to map the full extent of its powers. Seen from a purely structural perspective, it can be used to reduce both the number and length of transitive dependencies squirming through a program.