I have three classes that are circular dependant to each other:
TestExecuter execute requests of TestScenario and save a report file using ReportGenerator class.
So:
- TestExecuter depends on ReportGenerator to generate the report
- ReportGenerator depends on TestScenario and on parameters set from TestExecuter.
- TestScenario depends on TestExecuter.
Can't figure out how to remove thoses dependencies.
public class TestExecuter {
ReportGenerator reportGenerator;
public void getReportGenerator() {
reportGenerator = ReportGenerator.getInstance();
reportGenerator.setParams(this.params);
/* this.params several parameters from TestExecuter class example this.owner */
}
public void setTestScenario (TestScenario ts) {
reportGenerator.setTestScenario(ts);
}
public void saveReport() {
reportGenerator.saveReport();
}
public void executeRequest() {
/* do things */
}
}
public class ReportGenerator{
public static ReportGenerator getInstance(){}
public void setParams(String params){}
public void setTestScenario (TestScenario ts){}
public void saveReport(){}
}
public class TestScenario {
TestExecuter testExecuter;
public TestScenario(TestExecuter te) {
this.testExecuter=te;
}
public void execute() {
testExecuter.executeRequest();
}
}
public class Main {
public static void main(String [] args) {
TestExecuter te = new TestExecuter();
TestScenario ts = new TestScenario(te);
ts.execute();
te.getReportGenerator();
te.setTestScenario(ts);
te.saveReport()
}
}
EDIT: in response to an answer, more details about my TestScenario class:
public class TestScenario {
private LinkedList<Test> testList;
TestExecuter testExecuter;
public TestScenario(TestExecuter te) {
this.testExecuter=te;
}
public void execute() {
for (Test test: testList) {
testExecuter.executeRequest(test);
}
}
}
public class Test {
private String testName;
private String testResult;
}
public class ReportData {
/*shall have all information of the TestScenario including the list of Test */
}
An example of the xml file to be generated in case of a scenario containing two tests:
<testScenario name="scenario1">
<test name="test1">
<result>false</result>
</test>
<test name="test1">
<result>true</result>
</test>
</testScenario >
Best Answer
Technically, you can resolve any cyclic dependency by using interfaces, as shown in the other answers. However, I recommend to rethink your design. I think it is not unlikely you can avoid the need for additional interfaces completely, while your design becomes even simpler.
I guess it is not necessary for a
ReportGenerator
to depend on aTestScenario
directly.TestScenario
seems to have two responsibilites: it is used for test execution, and it works also as a container for the results. This is a violation of the SRP. Interestingly, by resolving that violation, you will get rid of the cyclic dependency as well.So instead of letting the report generator grab data from the test scenario, pass the data explicitly by using some value object. That means, replace
by some code like
The method
getReportData
needs to have a return type likeReportData
, a value object which works as a container for the data to be displayed in the report.insertDataToDisplay
is a method which expects an object of exactly that type.This way,
ReportGenerator
andTestScenario
will both depend onReportData
, which depends on nothing else, and the first two classes do not depend on each other any more.As a second approach: to resolve the SRP violation let
TestScenario
be responsible for holding the results of a test execution, but not for calling the test executer. Consider to reorganize the code so not the test scenario accesses the test executer, but the test executer is started from outside and writes the results back into theTestScenario
object. In the example you showed us, that will be possible by making the access toLinkedList<Test>
inside ofTestScenario
public, and by moving theexecute
method fromTestScenario
to somewhere else, maybe directly into aTestExecuter
, maybe into a new classTestScenarioExecuter
.That way,
TestExecuter
will depend onTestScenario
andReportGenerator
,ReportGenerator
will depend onTestScenario
, too , butTestScenario
will depend on nothing else.And finally, a third approach:
TestExecuter
has too many responsibilities, too. It is responsible for executing tests as well as for providing aTestScenario
to aReportGenerator
. Put these two responsibilities into two separate classes, and your cyclic dependency will vanish again.There may be more variants to approach your problem, but I hope you get the general idea: your core problem are classes with too many responsibilities. Solve that problem, and you will get rid of the cyclic dependency automatically.