Resolving Cyclic Dependencies

Need to break the vicious circle?

Need to break the vicious circle?

Cyclic dependencies among Classes are a common issue in software development. They are best resolved at design level. This article explains how to do this. It also provides some intuitive insights into Interfaces and dependencies.

Yesterday I wrote about dependencies and how they are displayed in UML. Today I build upon that basic knowledge and show you how to resolve cyclic dependencies among your classes.

Code vs. Design level

Visualizing a circle in 2-dimensional diagrams is pretty easy. But how would you do it in a 1-Dimensional code sequence?

Visualizing a circle in 2-dimensional diagrams is pretty easy. But how would you do it in a 1-Dimensional code sequence?

I often see programmers who try to solve this problem by discussing about source code. But this is clearly a design problem and is much easier to solve on design level.

One reason is that code (i.e. text) is inherently sequential, while graphical notations are 2-dimensional. Cycles are easy to display in 2D-space, but it’s impossible to display them in a sequence (i.e. 1D-space).

Restaurant example

Let’s take a simple example. We have a restaurant which needs to know its guests and people who need to know the restaurant they are visiting this evening. We model this as two classes, Restaurant and Person, which store each other in a field:

As an example we have a Person class, which references a Restaurant and a Restaurant class which references a Person.

As an example we have a Person class, which references a Restaurant and a Restaurant class which references a Person.

Our cyclic dependency is visible better, when using arrows to visualize the associations:

We can use arrows to the references better.

We can use arrows to the references better. The cyclic dependency becomes visible.

How to resolve the cycle?

Introducing an Interface

We can resolve the cycle by using a simple trick called Inversion of Dependency.

First, we need to decide in which direction the dependency should preferably point. Let’s say the Restaurant wants to know everything about the Person, because storing abnormal amounts of personal data is trendy. But the Person doesn’t need to know all details about the Restaurant, it just needs an address.

Secondly we just put a new interface between the two classes:

By simply inserting an Interface, we can resolve the cyclic dependency.

By simply inserting an Interface, we can resolve the cyclic dependency.

Now the Person doesn’t depend on the Restaurant anymore. Instead both classes depend on the new Interface. The “dependency flow” isn’t cyclic anymore.

A second Interface

You could break the dependencies further, by introducing another interface, but this is not necessary in most cases. I’ll show it here anyway, just so you have seen it once:

Inserting a second Interface is not required in most cases. But it can improve our understanding of interfaces and dependencies.

Inserting a second Interface is not required in most cases. But it can improve our understanding of interfaces and dependencies.

All arrows point to the interfaces between the classes now.

Although this step was not necessary, the result can help you get a feeling for interfaces. This example shows three things one needs to develop an intuition for:

  • Interfaces are sinks for the dependency flow. Often, indirect dependencies end there.
  • Inserting an Interface breaks the dependency flow by splitting a dependency in two and inverting one dependency.
  • Interfaces decouple classes. Classes separated by interfaces only depend on what is communicated, not on how or by whom.

Larger Cycles

This trick works the same way for cycles including more classes. For example, you could resolve the following cycle

Circles can also contain more than two classes. This makes even clearer why we need a graphical notation to recognize them.

Circles can also contain more than two classes. This makes even clearer why we need a graphical notation to recognize them.

by introducing an a new interface:

Larger circles are resolved the same way - by inserting an Interface.

Larger circles are resolved the same way – by inserting an Interface.

Open Problems

Initializing the Interface

Our Person from the previous example only requires a Location now. But still the Location needs to be instantiated with an object of a concrete class (i.e. Restaurant). If the Person would construct the Restaurant object itself, just to save it in a Location variable, the dependency to Restaurant would be revived.

Instead, you could either use dependency injection or some creational design pattern, like the abstract factory pattern. Neither of those will be covered in this article.

More complex examples

The above example was overly simplified. In reality, your interfaces often are not total sinks, but have some dependencies themselves. For example, the getAddress() method of Location may return an Address class. This means that the Location requires the existence of an Address class and is thus dependent on Address.

Interfaces can still depend on other classes and thus lead the dependency flow back into the code. But this are very "weak" dependencies.

Interfaces can still depend on other classes and thus lead the dependency flow back into the code. But this are very “weak” dependencies.

Also interfaces can inherit from other interfaces:

Interfaces can still inherit from other interfaces which then receive the dependency flow. But this keeps dependencies inside your interfaces and away from the executable classes.

Interfaces can still inherit from other interfaces which then receive the dependency flow. But this keeps dependencies inside your interfaces and away from the executable classes.

It may seem like I was misleading you when I told you that Interfaces act like dependency sinks. But it’s still partially true. In fact, the remaining dependencies are very “weak” forms of dependencies.

The inheritance only passes the dependency to another interface, which is an “almost” sink itself.

The dependency to another class only required the simple existence of that class. You could modify Address however you like. As long as it remains an Address, the Interface is not affected.

If even the simple class existence dependency is too much, you can invert the bothersome dependency like we did before:

To resolve interfaces depending on classes, you can apply Dependency Inversion.

To resolve interfaces depending on classes, you can apply Dependency Inversion.

Now you reduced the dependency on the existence of a class to a dependency on the existence of an interface. Thus, the sink only depends on another sink.

Conclusion

You now know how to resolve cyclic dependencies on a design level. You also have got some intuitive understanding of what interfaces are and how to use them. It was also mentioned that different kinds of dependencies exist and why some are weaker than others.

 

Cheers,

Waog

 

 

 

Advertisement

2 thoughts on “Resolving Cyclic Dependencies

  1. Pingback: Beyond coding – Levels of Errors in Software Development – Part 1 | Waog

  2. Pingback: Dependencies and Arrowheads | Waog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s