Converting a Cyclic Dependency into a Directed Dependency
So, this came out over a month lateâĶ Woops.
This is definitely not my original idea in the least. I wish I knew where I originally found this so I could share that, but seeing that I canât find it and I havenât seen it anywhere else, Iâd like to share this idea so it can become more well-known.
What is a cyclic dependency? It is one of mutual dependency, where one class depends on another that also depends on the first one, as shown below.
There can also be more classes in between as shown below, where it just eventually circles back to the beginning.
You probably know this, but in case you donât, you should know that you donât want these types of situations. They hurt the understandability of your system and make it harder for garbage collectors to clean them up when theyâre done. There are probably more reasons, too, but Iâve never paid much attention to this, and it seems that as a whole, the community knows that they should avoid cyclic dependencies but doesnât do much to avoid it.
Anyway, what youâre looking for is a nice acyclic directed graph of dependencies as shown below, where all the arrows point down.
So, how do we go from this
To an acyclic directed graph? What would that graph look like?
Well, it would look like this!
You take the part of the class that is depended on by the other class and extract it. Do this for both classes, and your problem is solved!
With these super vague graph images, it may be difficult to really see how this can actually be done, so Iâll give you a really simple code example (written in Kotlin and Python!). It should help you get started when breaking apart your cyclic dependencies.
class A {
var b: B? = null
var _observed: Int = 0
var observed: Int
get() = _observed
set(value) {
_observed = value
b?.alert()
}
fun alert(): Unit {
println("A.alert")
}
fun doSomething(): Unit {
alert()
}
}
class B {
var a: A? = null
var _observed: Int = 0
var observed: Int
get() = _observed
set(value) {
_observed = value
a?.alert()
}
fun alert(): Unit {
println("B.alert")
}
fun doSomething(): Unit {
alert()
}
}
class A:
def __init__(self):
self.b: B = None
self._observed: int = 0
@property
def observed(self):
return self._observed
@observed.setter
def observed(self, value):
self._observed = value
self.b.alert()
def alert(self):
print("A.alert")
def doSomething(self):
self.alert()
class B:
def __init__(self):
self.a: A = None
self._observed: int = 0
@property
def observed(self):
return self._observed
@observed.setter
def observed(self, value):
self._observed = value
self.a.alert()
def alert(self):
print("B.alert")
def doSomething(self):
self.alert()The two classes are almost exactly the same, but that doesnât really matter. What matters is that the dependent parts can be extracted. What parts of type B does A depend on? And vice versa? Each class depends on the otherâs alert() method. So letâs extract those out:
class AAlerter {
fun alert(): Unit {
println("A.alert")
}
}
class BAlerter {
fun alert(): Unit {
println("B.alert")
}
}
class AAlerter:
def alert(self):
print("A.alert")
class BAlerter:
def alert(self):
print("B.alert")Now the other classes can depend on these
class A (var a: AAlerter, var b: BAlerter) {
var _observed: Int = 0
var observed: Int
get() = _observed
set(value) {
_observed = value
b.alert()
}
fun doSomething(): Unit {
a.alert()
}
}
class B (var a: AAlerter, var b: BAlerter){
var _observed: Int = 0
var observed: Int
get() = _observed
set(value) {
_observed = value
a.alert()
}
fun doSomething(): Unit {
b.alert()
}
}
class A:
def __init__(self, a: AAlerter, b: BAlerter):
self.a = a
self.b = b
self._observed: int = 0
@property
def observed(self):
return self._observed
@observed.setter
def observed(self, value):
self._observed = value
self.b.alert()
def doSomething(self):
self.a.alert()
class B:
def __init__(self, a: AAlerter, b: BAlerter):
self.a = a
self.b = b
self._observed: int = 0
@property
def observed(self):
return self._observed
@observed.setter
def observed(self, value):
self._observed = value
self.a.alert()
def doSomething(self):
self.b.alert()You may notice that, due to the cyclic dependencies, there was no way to create instances of the original classes without null/None because each one would require an instance to exist in order to make it.
Now, itâs possible to create an instance where the constructor takes in all of the fields without any temporary nulls/Nones.
Outro
As I said before, I realize that this is a super simple example, and I donât expect it to make you into experts on removing cyclic dependencies. What I do expect is that you now have your mind wrapped around the basics and can start trying to take apart some of these when you see them.
I will admit that there is at least one âkindâ of cyclic dependency that this doesnât fix: A child pointing back to its parent. For example, you have a FilingCabinet with a list of Files in it, and those Files also have a pointer back to the FilingCabinet theyâre in if you ever need a way to traverse back up the tree when you donât know the original already.
The advice Iâve seen on this is to lose the link going back to the parent and instead put in a method that does some kind of lookup to find the parent. This is silly; it still has the cyclic dependency; itâs just that the dependency is either one step further removed (for a fully in-memory, in-language lookup) or is pulled into a different kind of system (for something like a database lookup).
I recommend either trying to make it so that the code doesnât need to go back up the tree or that the parent is passed in along with the child, possibly in some sort of custom parent-child pair type.
| Published on Java Code Geeks with permission by Jacob Zimmerman, partner at our JCG program. See the original article here: Converting a Cyclic Dependency into a Directed Dependency Opinions expressed by Java Code Geeks contributors are their own. |







