J'ai trois classes circulaires qui dépendent les unes des autres:
TestExecuter exécute les requêtes de TestScenario et enregistre un fichier de rapport à l'aide de la classe ReportGenerator. Donc:
Impossible de comprendre comment supprimer ces dépendances.
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: en réponse à une réponse, plus de détails sur ma classe TestScenario:
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 */
}
Un exemple du fichier xml à générer en cas de scénario contenant deux tests:
<testScenario name="scenario1">
<test name="test1">
<result>false</result>
</test>
<test name="test1">
<result>true</result>
</test>
</testScenario >
Techniquement, vous pouvez résoudre toute dépendance cyclique en utilisant des interfaces, comme indiqué dans les autres réponses. Cependant, je recommande de repenser votre conception. Je pense qu'il n'est pas improbable que vous puissiez éviter le besoin d'interfaces supplémentaires complètement, tandis que votre conception devient encore plus simple.
Je suppose qu'il n'est pas nécessaire qu'un ReportGenerator
dépende directement d'un TestScenario
. TestScenario
semble avoir deux responsabilités: il est utilisé pour l'exécution des tests, et il fonctionne également comme un conteneur pour les résultats. Il s'agit d'une violation du SRP. Fait intéressant, en résolvant cette violation, vous vous débarrasserez également de la dépendance cyclique.
Ainsi, au lieu de laisser le générateur de rapports récupérer les données du scénario de test, passez les données explicitement en utilisant un objet valeur. Cela signifie, remplacer
reportGenerator.setTestScenario(ts);
par un code comme
reportGenerator.insertDataToDisplay(ts.getReportData());
La méthode getReportData
doit avoir un type de retour comme ReportData
, un objet valeur qui fonctionne comme un conteneur pour les données à afficher dans le rapport. insertDataToDisplay
est une méthode qui attend un objet exactement de ce type.
De cette façon, ReportGenerator
et TestScenario
dépendront tous deux de ReportData
, qui ne dépend de rien d'autre, et les deux premières classes ne dépendent plus l'une de l'autre.
Comme deuxième approche: pour résoudre la violation SRP, laissez TestScenario
être responsable de la conservation des résultats d'une exécution de test, mais pas de l'appel de l'exécuteur de test. Envisagez de réorganiser le code afin que le scénario de test n'accède pas à l'exécuteur de test, mais l'exécuteur de test est démarré de l'extérieur et réécrit les résultats dans l'objet TestScenario
. Dans l'exemple que vous nous avez montré, cela sera possible en rendant l'accès à LinkedList<Test>
à l'intérieur de TestScenario
public, et en déplaçant la méthode execute
de TestScenario
ailleurs, peut-être directement dans un TestExecuter
, peut-être dans une nouvelle classe TestScenarioExecuter
.
De cette façon, TestExecuter
dépendra de TestScenario
et ReportGenerator
, ReportGenerator
dépendra également de TestScenario
, mais TestScenario
ne dépendra de rien d'autre.
Et enfin, une troisième approche: TestExecuter
a trop de responsabilités aussi. Il est responsable de l'exécution des tests ainsi que de la fourniture d'un TestScenario
à un ReportGenerator
. Mettez ces deux responsabilités dans deux classes distinctes et votre dépendance cyclique disparaîtra à nouveau.
Il peut y avoir plus de variantes pour aborder votre problème, mais j'espère que vous aurez l'idée générale: votre problème principal est les classes avec trop de responsabilités. Résolvez ce problème et vous vous débarrasserez automatiquement de la dépendance cyclique.
En utilisant des interfaces, vous pouvez résoudre la dépendance circulaire.
Conception actuelle:
Conception proposée:
Dans la conception proposée, les classes concrètes ne dépendent pas d'autres classes concrètes, mais uniquement d'abstractions (interfaces).
Important:
Vous devez utiliser le motif de création de votre choix (peut-être une usine) pour éviter de parfaire new
de toutes les classes de béton à l'intérieur de tout autre béton ou en appelant getInstance()
. Seule l'usine aura des dépendances sur des classes concrètes. Votre classe Main
pourrait servir d'usine si vous pensez qu'une usine dédiée serait exagérée. Par exemple, vous pouvez injecter un ReportGenerator
dans TestExecuter
au lieu d'appeler getInstance()
ou new
.
Puisque TestExecutor
utilise uniquement ReportGenerator
en interne, vous devriez pouvoir définir une interface pour celle-ci et vous référer à l'interface dans TestScenario
. Alors TestExecutor
dépend de ReportGenerator
, ReportGenerator
dépend de TestScenario
, et TestScenario
dépend de ITestExecutor
, ce qui ne fonctionne pas ' t dépendre de rien.
Idéalement, vous définiriez des interfaces pour toutes vos classes et exprimeriez les dépendances à travers elles, mais c'est le plus petit changement qui résoudra votre problème.