Circular Dependencies

What is a circular dependency and how can I solve it?

What is a dependency?

In order to understand what circular dependency is, it is better to understand what is a dependency and what it means to the compiler.

Let's say you have a project and, in a class, you have the following defined:

Public Class MyClass
'Some code here
Private MyString As String
'Some code there
End Class

When compiling your project, the compiler runs into the String class, which is defined in a DLL file called System. It will then link that DLL to your project, so at run-time, when defining or doing operation on the string, the System.dll will be loaded to perform those.

Now, let's say you have, further in your class, the following definition

'Some code here
Private MyObjet as CustomClass1
'Some code there

And let's say CustomClass1 is defined in another project of yours, named Project2.DLL:

Public Class CustomClass1
'Your custom class code
End Class

So when compiling your first project, the compiler will run into CustomClass1 definition, it knows it lays into Project2.dll and therefore will compile Project2 before, in order to be able to add that reference in your first project.

That's what a dependency is, it's hierarchical, there must be a starting point. Even the String class is dependant on other classes, and at the end, they all rely on bytes or bits to do the job, because that's the only thing a computer can do, play with 1 and 0.

So the circular part

So if you have, in Project2, a reference (a field definition, or something like that) that link to your first project, what happens?

  • The compiler reads your first project, then runs into CustomClass1
  • Then it tries to compile Project2, since CustomClass1 is defined there
  • Then it runs to a class defined in your first project
  • It tries to compile your first project in order to link it to the second
  • Then it runs to CustomClass1
  • Then it tried to compile Project2
  • I guess you got it...

So at some point the compiler displays an error, saying it cannot compile, as it doesn't understand what you're trying to do...

Yes, computers are that stupid.

How to solve it ?

Solving these kind of issue is sometimes difficult, but the basic idea is to build up a hierarchical structure, put the base class (those which don't need dependencies) together, then build up on them.

Take all the classes that depend on each other and put them together, they form a layer for something you try to do in your application.

How to resolve circular dependencies in typescript

Sorry for the misunderstanding about naming. Unfortunately, I had a chance to see similar names in real apps and somehow wrongly assumed that you also want to use this convention. When it comes to "ending up either with huge files that have everything piled up or super-tiny files". This is a matter of finding a good balance. I don't mind a lot of small files (js modules), that are focused on a single functionality - it is a sign that one has correctly distilled smaller responsibilities from some bigger use case. This produces code that is simpler to understand, test and maintain. The big files (js modules) or big classes/functions are often a sign that SRP is broken. Regarding sandbox.io example - I can't wrap my head around it and don't understand the intentions behind hello and world functions. They are just simple functions that recursively call each other (causing stack overflow). The simplest refactor would be to just use a shared function like e.g. buildGreeting(msg1, msg2) placed in foobar directory. Export const world = 'world' from foo directory, and const hello = 'hello' from bar directory, then in some other sibling directory create a module with a call like:


import {hello} from '../foo'
import {word} from '../bar'

buildGreeting(hello, word);

However, it is challenging to illustrate any meaningful improvement over this example code, because it does not illustrate any real use case.

circular dependency in web application using spring boot security

you can write one line in your application.properties file to remove this error.

spring.main.allow-circular-references= true

How do node.js circular dependencies work

The module system in Node.js does not support circular dependency and this is by design. In your original case you have accidentally tricked Node into a circular dependency situation by lazily requiring test1 (you only require it once the function is called).

You need to design your code so that dependencies only happen in one direction. Pass the thing that the other module need normally using functions instead of dependencies.

test1.js:

const { registerX, doStuff } = require("./test2.js");

let x = { value: 1 }; // need something that is not
// a number or string since we
// need a reference (pointer)
// instead of a value

registerX(x); // we pass x to test2 module

setInterval(() => {
console.log("Test1 :", x.value);
x.value++;
doStuff();
}, 1000);

test2.js:

let x = {};

function registerX (otherX) {
x = otherX;
}

function doStuff(){
console.log("Test2 :", x.value);
}

module.exports = {
registerX,
doStuff
};

Is circular dependency good or bad

The problem with circular dependencies is rather like the chicken and egg problem.

If you depend on me setting something up, and I depend on you setting something up, how do we start?

The corollary of this is how do we end - if I have a reference to your resource and you have a reference to mine, I can never clean up because that would break you, and you cannot clean up because that would break me.

The answer in both cases is to introduce a middleman, passing the dependency from one of the parties to him, So if you passed your resource on to the middleman, you would depend on me and the middleman, and I would depend on the middleman. Thus you can clean up because you now hold no resource, and I can clean up because no-one depends on me, and then the middleman can clean up.

How to resolve circular dependencies when using go modules and cgo

If you look at the verbose output from the go build command, you will see that when compiling the directory as a complete go package, the main.c file is being included as part of the C code used in hello.go.

From the documentation:

When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package

The easiest solution here is to separate the main C and Go packages, so that they don't interfere with each other's build process. Testing this out, removing the main.c file will build libchello.a and libgohello.a, and then adding it back in will complete the build of main.

How to setup seemly circular but not actually circular dependencies in Azure Data Factory V2

I provide below ideas for your reference:

1.Firstly,put the pipelines into Execute Pipeline Activity because you may have to do some steps before and after execution of A and B pipelines.

2.Secondly,since the pipelines have to be executed with some conditions,i think you could persist the results of A and B pipelines execution anyway.For example,after executing pipeline,use an Azure Function Activity or Web Activity to send the result of pipeline into some residences. The aim is logging the latest execute result of A and B.

3.Finally,before the execution of A and B pipelines,you could use an Until Activity to evaluate whether the pipeline could be executed.

Circular dependencies between classes: why they are bad and how to get rid of them?

Why are circular dependencies (CiDs) bad?

Two reasons:

  1. Maintainability.

You want your code to be layered, i.e. you want to have a top-down diagram of dependencies (a diagram showing all arrows going down, and no arrows going up).
If you have CiDs, your code is not layered.

Why does the layered code mean maintainability? Because, every time you change the interface of a
class, you can be sure that nothing below it will be affected. This knowledge makes maintenance and development
of the system cheaper and less error-prone.

dependency diagram example

Layerlens can auto-generate dependency diagrams for your project.


  1. Reliability.

You do not want unexpected infinite recursion in production. For example, when your configuration wants to
log an error and your error-logger wants to read the name of the log file from configuration (your tests will
pass, because there is no error in the test environment).
(Unfortunately, unexpected recursion can be
developed even without CiDs.)

Are there good CiDs?

Some CiDs are valid, helpful, and do not affect maintainability or reliability: String and Object, File and Folder, Node and Edge. Usually, such circles sit within one package and do not contribute into circular dependencies between packages.

How do I detect package CiDs?

You can detect CiDs visually on the diagram of dependencies (see links to generation tools above).

If your project is huge or you want to watch for CiDs continuously, it is simple to implement a tool that uses
reflection to detect CiDs (traverse classes or packages depth-first and stop at the first back reference).

Note, that if one package is declared inside other, this does not mean they depend on each other, unless classes from them reference each other.

My project is a dependency mess. Is there a methodology to fix it?

dependency mess
Yes, there is. Here are the steps:

  1. Design the desired structure.

a. Create a plain list of existing packages, like this:
Sample Image

If your package structure is hierarchical, flatten it, and do not forget the root package.

b. Organize the packages.

Re-order the list so that packages with a lower abstraction level are closer to the bottom and packages with a
higher abstraction level are closer to the top.
If a package contains classes of both low and high abstraction levels, you may want to break this package into two.

If you have too many packages, first break them into layers, order the layers, then order the packages inside those layers.
This step should result in your desired order of packages, where you want the dependencies to go down and not up.

c. Organize classes in the packages the same way.


  1. Make the process measurable.

Measure the distance to the desired structure so that you can see the progress as you get closer to the goal.
For each class, count the number of wrong dependencies (dependencies directed up). The sum of these numbers will
become your metric. At the end, it will be zero.

Set up monitoring to detect new wrong dependencies. You want to stop your teammates (and yourself) before they
are about to check in another wrong dependency.


  1. Resolve wrong dependencies one by one. Usually it is easier to go from bottom to top, as you’ll want to clarify
    the basics first.

These tips may help:

A. You have a "death star” or “god object,” i.e. an object that is known by classes it knows about. In
other words, such a class is a part of many circular dependencies which may result in the dependency of each
class on almost every other class.

Solution:

Most death stars can be resolved by splitting them into two (or more) classes, where one class contains just
state and very basic operations and another class contains advanced operations (usually the second class is static).
Sample Image

B. You do not have a circle between classes, just between packages.

Solution:

Consider moving some classes to other packages.

C. You have a class that uses some methods of upper class, but does not own its instantiation.

Solution:

Use a callback interface or callback method (pattern Observer).
Sample Image

D. You have two classes that create each other and use each other’s methods.

Solutions:

  • Combine the classes into one class.
  • Put the classes into one package and declare their relationship as a good CiD.
  • Create a Factory class and interface for one of the classes.

Sample Image

How can I maintain clarity in my project?

If you have a small team and a small project, simply explain the rules to your teammates and occasionally check the diagram.

If the project is large and complicated, you should establish a process to receive an alert or rejection every time someone
compiles or checks in a wrong dependency.



Related Topics



Leave a reply



Submit