If you've used dependency injection, at some point you've probably run across a dependency graph cycle, also known as a circular dependency. This post looks at why cycles can be problematic and ways of dealing with them.
Basic Theory
When configuring your Dependency Injection container, you're setting up a directed graph (read if you're not familiar) where the vertices are classes and the edges are dependencies between classes. A cycle in the graph means that somehow there's a mutual dependency among two or more classes. In this post we'll explain why you always want your dependency graph to be a DAG (Directed Acyclic Graph) and how to deal with situations where cycles arise.
Constructor Injection
Practically speaking, you'll likely encounter a would-be dependency graph cycle as a side effect of how most dependency injection frameworks work by default. Usually when a class needs a particular interface it simply makes that interface a constructor parameter, but when there's a dependency graph cycle this no longer works. For example:
interface IA {
// Members
}
interface IB {
// Members
}
class A implements IA {
private final IB mB;
public A(IB b) {
mB = b;
}
// Members
}
class B implements IB {
private final IA mA;
public B(IA a) {
mA = a;
}
// Members
}
Class A needs the IB implementation, and class B needs the IA implementation, but neither can be instantiated because they depend on one another. As you can see, constructor injection does not directly allow dependency graph cycles.
There are ways such as setter injection to create working cycles in your dependency graph, but that isn't really a solution to the underlying design problem. Cycles point to several potential problems in your code, so consider constructor injection's inability to directly support cycles a helpful design tool rather than a bug.
Issues with Cycles
To understand why we don't want cycles in the class dependency graph, we need to look at the method dependency graph. We'll look at examples of the two possible scenarios of graph cycles, whey they're an issue, and how to fix them. Note that these diagrams show both the class dependency graph and the method dependency graph.
Acyclic Method Dependency Graph
In this example notice that the method dependency graph itself does not actually have any cycles, even though the class dependency graph does. In this case, method 2 is really its own responsibility, and class A needs to be broken down further.
Eliminating this class dependency cycle forced us to break the application into smaller pieces, thus improving the application.
Cyclic Method Dependency Graph
This example is rarer and indicates a much bigger problem, because there's actually a cycle in the method dependency graph indicating a likely mutual recursion. The solution can actually be very different depending on whether or not the recursion was intentional.
Solution to Intentional Recursion
If this mutually recursive behavior was intentional, then the solution is to separate the recursive/iterative functionality from the "guts" of the individual methods. Here is one possible solution:
In this example, we've converted the entire recursive aspect of the algorithm into a loop within method 1. The original algorithm probably had four parts because there are really four different pieces of logic to attend to, so in the example we pull those four different parts into new non-recursive methods.
Solution to Unintentional Recursion
If you didn't originally intend to create a mutual recursion then you're likely in for stack overflows and/or major performance problems. In the majority of cases, this is a bug more than a design issue. Unfortunately the actual fix is going to depend completely on your application, though breaking down methods into smaller pieces can often help identify the root cause. Sometimes the cycle in the method dependency graph won't actually create a mutual recursion as a result of the logic, but that situation can later change unexpectedly as a result of a seemingly innocuous modification to the code.
This issue can happen when initialization logic and processing logic are too intertwined. Try moving initialization into a separate class from the one that needs to use the initialized state or configuration. This can help clarify order of operations.
Wrapping Up
Encountering a circular dependency error from your dependency injection container is never fun, but taking the easy road and converting the constructor injection to setter injection is a great way to cause yourself pain later. The majority of the time, circular dependencies happen because some part of the application needed to be broken down into smaller pieces.
I hope these ideas are clear enough to be helpful. Please let me know what you think in the comments.
The Star Grand at The Star Gold Coast - JT Hub
ReplyDeleteGold Coast's 계룡 출장샵 Grand Grand at The Star Gold 의정부 출장안마 Coast offers the latest in integrated 부산광역 출장안마 resort hotel 당진 출장마사지 design that complements 성남 출장샵 the existing Wynn
It is what I was searching for is really informative.keyword It is a significant and useful article for us. Thankful to you for sharing an article like this.">Deca Durabolin 10ml injection for muscle growth It is a significant and useful article for us. Thankful to you for sharing an article like this.
ReplyDeleteIf you're feeling casino.edu.kg like you're getting very annoyed and angry, flip the on line casino off and take a break. The opinions of Jungle Jim to me are what I even have have} been saying all alongside as nicely. Slot machines are additionally known pejoratively as one-armed bandits due to the massive mechanical levers affixed to the sides of early mechanical machines. Take a walk on the wild side of slots with the distinctive vary of video games from NoLimit City. Play some of the the} grittiest slots obtainable right now and win real cash on MrQ. Make right now the time to play with the highest video games from Pragmatic Play!
ReplyDeleteBut each on line casino has a thecasinosource.com rule book have the ability to|you presumably can} seek the advice of} so you understand what’s being dealt is the standard. Electronic table video games, or ETGs, had been greeted with lackluster enthusiasm when they had been first introduced to Las Vegas gamblers in early 2000. Compared to unfastened slot machines, these alternatives had been frowned upon initially.
ReplyDelete