The principle of Dependency Inversion is a useful design pattern to be more flexible about dependencies. It is helpful in a layered architecture where high-level packages can depend on low-level packages but not the other way round. The observer pattern used in Model-View-Controller is a well-known example. We can visualize it like this:

img/dependency_inversion.svg

On the left you can see that class X uses class Y directly, so package A depends on package B. This is a problem because A is lower level and should not depend on B. To invert that dependency, package A can define an interface Iy which Y in B implements (assuming Java semantics). Now package B depends on package A and we have inverted the relationship. Informally, arrows between packages going down is good.

Note that package B implements and package A defines the interface Iy. Interfaces should usually be defined where they are used, not where they are implemented.

Generalizing Design What can you do with packages on the same layer? Assume we have a package C which uses A and B. Within C the two components A and B shall interact. We also want A and B to be self contained. Neither of them should depend on the other or on C. For example, there could be D, E, F, and G which are like C but use different versions of A and B. Any direct dependency between A and B leads to problems in such a scenario. A cheap solution is to introduce a common Base package. img/base_copout.svg The risk here is that it might not scale well for D, E, F, and G, because all need to use the same version if Base. You probably cannot update package A without package B if it requires an update of Base as well. Think again. We can apply the same trick of dependency inversion: A defines an interface for B and vice versa. Now C can create adapter classes for the interaction and thus there is no dependency between A and B. img/same_layer_dependency.svg D, E, F, and G can also create their own adapters and depending on which versions of A and B they use, everything just modifies its adapters. I do concede that this feels overengineered. Especially when interface and corresponding class are nearly identical because then the adapter class consists of trivial methods which just redirect to a member. This means you have to consider the cost before you apply the pattern. This is a generalization of Dependency Inversion which I would name "Dependency Abstraction" since we abstract the dependency to an interface. Tweet This Dependency Abstraction: Instead of depending on something external, define yourself an abstract interface. The special case that "something external" is on a layer above is dependency inversion. If that "something external" is on a layer below, it is probably not be necessary. Well, you can stub the lower level for testing so it might still be useful. If "something external" is on the same layer define an interface and use an adapter on higher levels.