20

I need to know why we need to avoid circular dependencies? In the real world if we think, circular dependencies are pretty much important. Like one friend needs something from other friend and the other needs something from this friend, so its kind of circular right?

Then why is circular dependency a bad design? If we really need to avoid this then what is the best possible design in Object oriented world for such a situation?

Nipun
  • 4,119
  • 5
  • 47
  • 83
  • 2
    In my experience the main problem with circular references is management of memory - who owns what? – Neil Kirk Nov 07 '14 at 15:43
  • It is neither good, nor bad : it is what it is. If feel you must, beware of memory leaks. – YvesLeBorg Nov 07 '14 at 15:45
  • last time I checked ( aka I had to compile that ) the `llvm` project was all in for circular dependencies and they are writing a virtual machine / compiler . You can take that as an example and maybe ask why they are going with that design . – user2485710 Nov 07 '14 at 15:45
  • Lets take an example with header file: Header file A needs something from header file B to work, and header file B needs something from header file A to work. But header file B can't include header file A, because header file A needs to include B first, which first needs to include A, and so on. This is a typical circular dependency that is very common among beginners in C and similar languages. – Some programmer dude Nov 07 '14 at 15:45
  • 1
    Your example of "... one friend needs something from other friend and the other needs something from this friend.." is not a _dependency_ unless the first friend _cannot do_ what the second needs unless they provide what the first friend needs. Without a dependency either party can provide the other with a service any time. So, generally, you want to avoid a circular dependency. – Tony Nov 07 '14 at 16:06

2 Answers2

36

The problem with circular dependencies is rather like the chicken and egg problem.

If you depend on me setting something up, and I depend on you setting something up, how do we start?

The corollary of this is how do we end - if I have a reference to your resource and you have a reference to mine, I can never clean up because that would break you, and you cannot clean up because that would break me.

The answer in both cases is to introduce a middleman, passing the dependency from one of the parties to him, So if you passed your resource on to the middleman, you would depend on me and the middleman, and I would depend on the middleman. Thus you can clean up because you now hold no resource, and I can clean up because no-one depends on me, and then the middleman can clean up.

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
  • 3
    good generic answer, avoids pitfalls of language specific do-hickies that may handle part of that complexity, and useful pattern in all cases i know of. +1 – YvesLeBorg Nov 07 '14 at 15:53
  • hmmm thanks gbjbaanb. But it also means that in virtual world 2 friends cannot interact with each other directly, they will need a middleman to do that – Nipun Nov 07 '14 at 15:54
  • You can still communicate, you just cannot *depend* on the other. So once you have such a setup, you must expect the partner to not be present. Its the tight coupling that matters in circular dependencies. – gbjbaanb Nov 07 '14 at 16:11
  • @Nipun: Friendship isn't a circular dependency. If I needed to wait for my friends to interact with me before I could interact with them, and they had to wait for me to start the interaction, that would be a circular dependency, but friendship doesn't work that way. – user2357112 Nov 07 '14 at 16:13
  • `The problem with circular dependencies is rather like the chicken and egg problem` I don't like this phrase, it sounds like we don't graphs but only trees at our disposal; there is also the difference between declaration and definition, I can't speak for all the languages out there but the circular dep. is often time relative to the declaration instead of the definition, with any modern compiler this problem is not only comparable to this but 100% solvable . For example forwarding a declaration can solve the circ. dep. problem, it's as easy as writing that . – user2485710 Nov 07 '14 at 16:17
  • Thanks....Loved the answers. Cleared my doubts – Nipun Nov 07 '14 at 16:19
  • Problem with the middleman is you generally have to expose public functions to that middleman such that the middleman can call it. That functions could be private with circular dependencies and forward declarations. – Ini Jun 23 '18 at 11:47
7

You need to realize that a circular dependency implies that you can use only the corresponding circular dependent classes together: if you have a circular dependency between, say, A and B, you cannot use A or B independently in any program. To stick with your problem: surely, you don't need the other two friends to exist! All you need is some way to refer to some and interact with them in a way which may be constrained over their actual abilities.

However, it often is possible to have objects of classes use each other without cause a circular dependency. To this end it is important to determine what actually causes a dependency between two class/components (these are not entirely equivalent but providing a thorough definition would be somewhat lengthy). A depends on B under these conditions:

  1. When A contains a member of type B.
  2. When A derives from type B.
  3. When A uses a value of type B as part of a function signature.
  4. When A uses B in its implementation.
  5. I probably forget something here (I recall there were more reason why classes would be coupled).

When you have a cyclic dependency between two classes, there may be ways to break this dependency. Often, the dependency can be broken by splitting one of the two classes into a base class and a derived class with the base class not depending on the other class. There are a number of other approaches to break dependency cycles. John Lakos's "Large Scale C++" (1996) is essentially all about break dependency cycles and motivating why cyclic dependencies are bad (I guess, he would disagree with this simplifying characterization).

... and, yes, cyclic dependencies are bad:

  1. They cause programs to include unnecessary functionality because things are dragged in which aren't needed.
  2. They make it a lot harder to test software.
  3. They make it a lot harder to reason about software.
  4. They make it a lot harder to replace parts of the system.
  5. ... and probably a number of other reasons.

The above is formulated with a view taken from a C++ perspective. Some of the causes of circular dependencies may not exist [directly] in C but the same concepts roughly apply to C, too.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380