A mutual dependency exists when two types depend directly on each other. Mutual dependencies are caused by unwanted dependencies in one or both directions. There are many different kinds of dependency; here are a few examples of how an inter-type dependency from T1 to T2 can occur:

Mutual dependencies prevent you from considering either entity in isolation, affecting readability and testability. For example, if types T1 and T2 depend on each other, then it is generally impossible to fully understand T1 without understanding T2, and vice-versa. Moreover, neither type can be tested without the other being present. Whilst mocking can alleviate this latter problem to some extent, breaking the mutual dependency is a better solution. For example, suppose we could remove all of the dependencies from T2 to T1 - in that case, we would be able to test T2 in isolation, and completely side-step the need to provide a T1, mocked or otherwise.

Breaking mutual dependencies involves finding ways of removing the unwanted individual dependencies that cause them. The way to do this depends on the kind of dependency in question, with some kinds (for example, dependencies caused by inheritance) being much harder to break than others. A full list of ways to break cycles is beyond the scope of this help topic, however, a few high-level techniques for breaking a dependency from T1 to T2 include:

For more information on how to break unwanted dependencies, see the references (particularly [Lakos]).

In this example BadModel and BadView are mutually dependent.

The interface technique can be used to break the dependency between the model and the view. The IModelListener interface allows BetterView to interact with BetterModel without dependency.

  • J. Lakos. Large-Scale C++ Software Design. Addison-Wesley, 1996.
  • M. Fowler. Refactoring. Addison-Wesley, 1999.