Dependency Inversion Principle

DIP:
A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions. (http://www.objectmentor.com/resources/articles/dip.pdf)

Forces

We wish to avoid designs which are: Inversion

"Structured" methods of the 1970's tended towards a "top-down decomposition", which encouraged high-level modules to depend on modules written at a lower level of abstraction. (More modern procedural approaches depend more on databases and less on functional decomposition at the large-scale level. Thus, they are often "flatter" now.) This principle stems from the realization that we must reverse the direction of these dependencies to avoid rigidity, fragility and immobility. [edited from Robert's original to reflect modern procedural philosophy]

Examples

In RobertMartins article, he uses a simple copying program as an example. The program copies characters from the keyboard to a printer. Modifying the program to support arbitrary output devices is inelegant since the copy "routine" implicitly depends on the keyboard reading code. A better architecture would define interfaces for readers and writers, thus the program can be easily adapted to support reading and writing from additional code for new devices. (This example seems to explain some of the idioms present in the java.io package.)

The Observer design pattern can be considered a two-layered architecture, where the model is the lower-level module and the higher level module is the observer.

See also:
One of the PrinciplesOfObjectOrientedDesign.


Controversies

Some of this sounds like typical pro-OO procedural-bashing. The "device driver" examples may not extrapolate to all situations and assume that adding new devices is more common than adding new operations to existing devices (ExtrapolatingDeviceDrivers). What is "rigid" often depends on assumed ChangePatterns. There is no free lunch in such cases, only estimations of paths of least costs.

And, too many layers of indirection is sometimes anti-YagNi.


In practice, what you mean by "dependency" will depend on the language. In Smalltalk, whatever handful of messages the client happens to send to a server is a protocol implicitly. It doesn't have to be named to satisfy the type checker. The client has no dependency on the server beyond them. So inserting the abstract base class buys you very little. You might as well just group the messages together in the IDE and label them there without adding the new class.

In C++, the traditional C++ and link formats mean that client code can depend on the size of the server object, the layout of its vtbl, and many other incidental details. The abstract base class is a way of making these details more stable.

It seems to me that the dependencies the inversion principle are trying to avoid are just artifacts of premature optimisation in the language implementation. -- DaveHarris


Circular dependencies between VisualBasic projects are worse than a nuisance -- they prevent you from being able to build executables from sources.

Specific examples?

Proper application of the DependencyInversionPrinciple -- via implementation of VB/COM interfaces -- resolves the problem.

Alternative for VisualBasic: Use "As Object" (IDispatch interface). Doing so will remove compile-time circular dependencies, at the expense of type safety and compile time type checking. (The projects will depend on IDispatch, rather than referencing each other.)


See also DependencyInversionAndXp and ContextIndependence
CategoryModellingLawsAndPrinciples

EditText of this page (last edited May 3, 2010)
FindPage by searching (or browse LikePages or take a VisualTour)