How to Remove Circular Reference When There Is Inter-Dependence

circular-dependencydesign-patternsobject-oriented-design

I made an extensive research in previousv related questions, but since my questions is somewhat peculiar, I decided to create this new one.

I am implementing a visualisation application (in JS). There is a class, called Graph, that contains the visualisation of a graph, along with all the necessary components (nodes, edges etc.). I decided to implement a clustering functionality over the Graph, that enables the user to cluster a group of nodes to a single cluster, depending on some attributes. I initially implemented this functionality in the Graph class. However, since it is a significant amount of logic, that is not directly related to the Graph, I think it should be properly refactored to a different component (I called it ClusteringEngine). And here comes the issue.

ClusteringEngine needs a reference to Graph, in order to retrieve nodes, edges etc. So, if ClusteringEngine is implemented as a sub-component of an outer class (a CentralController), the dependency graph would be like:

CentralController ---> Graph <----\
                 \                 \
                  \--> ClusteringEngine

which seems highly coupled. Another option is to implement ClusteringEngine as a component of Graph, where I will end up with a circular reference:

CentralController -----> Graph <-----> ClusteringEngine

which is again highly coupled.
I was wondering if there is a way to avoid this problem and design it in a better way.

P.S. I have thought of the Observer Design Pattern, but since there is no context of events-messages, I think it is not so appropriate for this case.

Best Answer

which seems highly coupled.

It's not highly coupled. In fact, it's the minimum coupling state. The ClusteringEngine will always depend on the Graph, no matter what you do. Furthermore, either the CentralController or the Graph must depend on the ClusteringEngine, else there won't be any clustering in the system. And finally, one considers that if the CentralController is handling e.g. user input, and the user input can cause the clustering, it's also totally unavoidable for the CentralController to depend on the ClusteringEngine.

Between these two choices, it's clearly better for the CentralController to depend on the ClusteringEngine. It's not circular and it's quite reasonable for the central controller to depend on things to make things happen, and it basically has to have that dependency anyway.

There's nothing wrong with your first alternative.