saurabh ranu
saurabh ranu

Reputation: 1371

How to remove circular dependency from two classes which are concrete types

I have two classes which are as follows :

public class A{
private String id ;
private SortedMap<String,B> answer = new TreeMap<String,B>();
private String text;
}

public class B{
private String id = null ;
private SortedMap<String,A> question = new TreeMap<String,A>();
private String text = null;
}

Is there any way i can remove circular dependency from the above classes..?

Upvotes: 2

Views: 7017

Answers (8)

Jan de Ruiter
Jan de Ruiter

Reputation: 111

(this is actually a comment, but I don't have enough reputation points to do that)

:->why would you like to do that?
because findbugs says so in Pattern: CD_CIRCULAR_DEPENDENCY: This class has a circular dependency with other classes. This makes building these classes difficult, as each is dependent on the other to build correctly. Consider using interfaces to break the hard dependency.

also wikipedia says: ...in software design circular dependencies between larger software modules are considered an anti-pattern because of their negative effects... Circular dependencies are often introduced by inexperienced programmers...

Upvotes: 1

David Armstrong
David Armstrong

Reputation: 11

If you have two classes which reference each other, javac will resolve that if you compile both at the same time:

file: dev/A.java
class A {
    B b = null;
    public A(B b)
    {
        this.b = b;
    }
};

file: dev/B.java
package dev;
class B {
    A a = null;
    public B(A a )
    {
        this.a = a;
    }
};

$ javac -d classes dev/A.java
dev/A.java:3: cannot find symbol
symbol  : class B
location: class dev.A
    B b = null;
^
dev/A.java:4: cannot find symbol
symbol  : class B
location: class dev.A
    public A(B b)
             ^
2 errors
$ javac -d classes dev/B.java
dev/B.java:3: cannot find symbol
symbol  : class A
location: class dev.B
    A a = null;
    ^
dev/B.java:4: cannot find symbol
symbol  : class A
location: class dev.B
    public B(A a ) 
         ^
2 errors`

but if you enter:

$ javac -d classes dev/A.java dev/B.java

it will resolve the circular compiler dependency.

Upvotes: 1

Ray Tayek
Ray Tayek

Reputation: 10003

try something like:

import java.util.*;
class Question {
    Question(int id, String question) {
        this.id = id;
        this.question = question;
    }
    static void toString(Question question, StringBuffer sb, int indent) {
        for(int i=0;i<indent;i++)
            sb.append('\t');
        sb.append(question.id).append(' ').append(question.question).append('\n');
        for (Map.Entry<Integer, Answer> entry : question.answers.entrySet()) {
            Answer answer = entry.getValue();
            for(int i=0;i<=indent;i++)
                sb.append('\t');
            sb.append(entry.getKey()).append(' ').append(answer.answer).append('\n');
            if (answer.question != null)  {
                toString(answer.question, sb, indent+2);
            }
        }
    }
    public String toString() {
        StringBuffer sb = new StringBuffer();
        toString(this,sb, 0);
        return sb.toString();
    }
    int id;
    String question;
    SortedMap<Integer, Answer> answers = new TreeMap<Integer, Answer>();
}
class Answer {
    Answer(int id, String answer) {
        this.id = id;
        this.answer = answer;
    }
    final int id;
    final String answer;
    Question question; // may be null
}
public class Main {
    public static void main(String[] args) {
        Question q0 = new Question(0, "What are you looking for?");
        Answer a0 = new Answer(0, "table");
        q0.answers.put(a0.id, a0);
        a0.question = new Question(0, "Which color table you want?");
        a0.question.answers.put(0, new Answer(0, "green"));
        System.out.println(q0);
    }
}

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533710

Based on what you have, you don't need two classes. Try to make the class more generic and you only need one.

public class AB {
    private final String id ;
    private final SortedMap<String,AB> answer = new TreeMap<String,AB>();
    private final String text;
    private final boolean isA; // if you need to know if its an A or B.
}

Upvotes: 1

Andreas Dolk
Andreas Dolk

Reputation: 114807

Looking back to the previous question - you could change the xml schema and add some sort of <nextquestion> tag to answers. The equivalent xml document then would be:

<decision>
  <question id="0">
    <questionText>What type is your OS?</questionText>
    <answer id="0">
      <answerText>windows</answerText>
    </answer>
    <answer id="1">
      <answerText>linux</answerText>
    </answer>
    <answer id="2">
      <answerText>mac</answerText>
    </answer>
  </question>
  <question id="1">
    <questionText>What are you looking for?</questionText>
    <answer id="0">
      <answerText>table</answerText>
      <!-- NEW TAG HERE -->
      <nextquestion refid="3" />
    </answer>
    <answer id="1">
      <answerText>chair</answerText>
    </answer>
    <answer id="2">
      <answerText>bed</answerText>
    </answer>
    <answer id="3">
      <answerText>cloth</answerText>
    </answer>
  </question>
  <!-- ALL QUESTIONS ARE CHILDREN OF ROOT WITH UNIQUE ID -->
  <question id="3">
    <questionText>Which color table you want?</questionText>
    <answer id="0">
      <answerText>green</answerText>
    </answer>
    <answer id="1">
      <answerText>black</answerText>
    </answer>
    <answer id="2">
      <answerText>pink</answerText>
    </answer>
  </question>
</decision>

You may want to use unique ids for the answers too or even reactor the model again if you want to reuse answers for different questions (many-to-many relationship)

And you classes:

public class Question {
  private int id;
  private String text;
  private Set<Answer> answers;
  // ...
}

public class Answer {
  private int id;
  private String text;
  private Question nextQuestion;
}

And of course there is a circular dependency but that is absolutely wanted and inherited from the models real-life domain.

Upvotes: 0

user425367
user425367

Reputation:

No there isn't but that's no problem.

There is no problem with having circular dependency in JAVA. It's quite common to have those if you want to traverse structures in both directions. Think a tree where the parent and child knows of each other thus creating a circular dependency.

The Garbage Collector will detect circular dependencies and handle this just fine.

The only problems occur when having circular dependencies in both constructors which will result in an stack overflow :)

Upvotes: 5

Virtosu Bogdan
Virtosu Bogdan

Reputation: 31

public class A{
private String id ;
private SortedMap<String,A> answer;
private String text;
}
public class B extends A{
} 

if you feel you need, or just use a boolean field in A

Upvotes: 0

Bozho
Bozho

Reputation: 597254

No, unless you remove one of the maps.

Upvotes: 1

Related Questions