Excessive dependencies are bad. But cyclic dependencies are especially bad. Cyclic dependencies are manifest in various ways at different levels within a system. It's also possible that acyclic relationships at one level cause cycles at another. Huh? Here's the basics.

Types of Cycles


Cycles exist across a variety of modules; notably class, package and .jar. Class cycles exist when two classes, such as Customer and Bill, each reference the other (assume Customer has a list of Bill instances, and Bill references the Customer to calculate a discount amount). Like this. This is also known as a bi-directional assocation. It's a maintenance and testing issue, since you can't do anything to either class without possibly affecting the other. Class cycles can be broken a few different ways, one of which is to introduce an abstraction that breaks the cycle. Like this.

Now you can test Bill with a mock DiscountCalculator. Testing Customer, of course, still requires the presence of Bill. This is not a cyclic issue, it's a different type coupling issue as Bill is a concrete class. Again, a separate discussion. Introducing DiscountCalculator has broken the cycle between Customer and Bill...but has it broken all cycles?

Creeping cycles


We don't intentionally create cyclic dependencies. Instead, they tend to creep into our design. They commonly surface when acyclic relationships at one level cause cycles at another. For instance, from the above example, if Customer and DiscountCalculator are placed in a cust package, and Bill is placed in a billing package, a cyclic dependency between cust and billing exists even though the class structure is acyclic. Allocating the cust and billing packages to cust.jar and bill.jar also causes a cycle between the .jars. Like this. To break the cycle, we should move DiscountCalculator to its own package, or the billing package. Like this. Simple heh? Well sure...but now toss in a few thousand classes, a few hundred packages, and numerous .jar files. It's not so simple to manage then.

Managing Cycles


Fortunately, there are many ways (some easier than others) to manage cycles. Test Driven Development is a great way to manage class cycles assuming we strive to test classes in isolation. JDepend allows you to manage package cycles, either by writing package constraint tests or including JDepend reports within your Ant build script. Jar cycles can be managed using a Levelized Build, where individual jars are built, including only necessary components in the build class path. JarAnalyzer can also be included in your build script, generating a component diagram illustrating the relationship between .jar files, or a dependency report similar to that of JDepend.

Are Cycles Always Bad?


Generally speaking, "Yes"! But at times, they are worse than others. Cycles among classes are tolerable, assuming they don't cause cycles among the packages or .jar files containing them (ie. the classes must be in the same package, essentially encapsulating the design). Cycles among packages may also be tolerable, assuming they don't cause cycles among the .jar files containing them (again, packages are in the same .jar file). Most important is that we are aware of the relationships existing between the modules. In so many cases, we aren't.