Comment résoudre la dépendance circulaire?
On janvier 1, 2021 by adminJai trois classes circulaires dépendantes les unes des autres:
TestExecuter exécute les requêtes de TestScenario et enregistre un fichier de rapport en utilisant la classe ReportGenerator . Donc:
- TestExecuter dépend de ReportGenerator pour générer le rapport
- ReportGenerator dépend de TestScenario et des paramètres définis à partir de TestExecuter.
- TestScenario dépend de TestExecuter.
Je narrive pas à trouver 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 de xml à générer dans le cas dun scénario contenant deux tests:
<testScenario name="scenario1"> <test name="test1"> <result>false</result> </test> <test name="test1"> <result>true</result> </test> </testScenario >
Commentaires
Réponse
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 quil nest pas improbable que vous puissiez éviter le besoin dinterfaces supplémentaires complètement, alors que votre conception devient encore plus simple.
Je suppose quil nest pas nécessaire quun ReportGenerator
dépende directement dun TestScenario
. TestScenario
semble avoir deux responsabilités: il est utilisé pour lexécution des tests, et il fonctionne également comme un conteneur pour les résultats. Ceci est une violation du SRP. Fait intéressant, en résolvant cette violation, vous vous débarrasserez également de la dépendance cyclique.
Donc, au lieu de laisser le générateur de rapports récupérer les données du scénario de test, transmettez les données explicitement en utilisant un objet de valeur. Cela signifie, remplacez
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 de 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épendra tous deux de ReportData
, qui ne dépend de rien dautre, et les deux premières classes ne dépendent plus lune de lautre.
Comme deuxième approche: pour résoudre la violation SRP, laissez TestScenario
être responsable de la conservation des résultats dune exécution de test, mais pas de lappel de lexécuteur de test. Pensez à réorganiser le code afin que le scénario de test naccède pas à lexécuteur de test, mais lexécuteur de test est lancé de lextérieur et réécrit les résultats dans lobjet TestScenario
. Dans lexemple que vous nous avez montré, cela sera possible en rendant laccès à LinkedList<Test>
à lintérieur de TestScenario
public, et en déplaçant le execute
méthode de TestScenario
vers un autre endroit, 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 dautre.
Et enfin, une troisième approche: TestExecuter
a aussi trop de responsabilités. Il est responsable de lexécution des tests ainsi que de la fourniture dun TestScenario
à un ReportGenerator
. Mettez ces deux responsabilités dans deux classes distinctes, et votre dépendance cyclique disparaîtra à nouveau.
Il y aura peut-être plus de variantes pour aborder votre problème, mais jespère que vous avez lidée générale: votre problème principal sont les classes avec trop de responsabilités . Résolvez ce problème et vous vous débarrasserez automatiquement de la dépendance cyclique.
Commentaires
- Merci pour votre réponse, en fait jai besoin de toutes les informations dans TestScenario pour pouvoir générer mon rapport à la fin 🙁
- @ sabrina2020: et quest-ce qui vous empêche de mettre toutes ces informations dans
ReportData
?Vous pouvez envisager de modifier votre question et dexpliquer un peu plus en détail ce qui se passe à lintérieur desaveReport
. - En fait, mon TestScenario contient une liste de Test et je veux tout informations dans un fichier xml de rapport afin que ReportData ait tout dans ce cas, je modifierai ma réponse pour plus de détails, merci!
- +1: Vous mavez eu à
interfaces
. - @ sabrina2020: Jai ajouté deux approches différentes à ma réponse, choisissez celle qui correspond le mieux à vos besoins.
Réponse
En utilisant des interfaces, vous pouvez résoudre la dépendance circulaire.
Conception actuelle:
Conception proposée:
Dans le béton de conception proposé les classes 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 les performances new
de toutes les classes concrètes à lintérieur de toute autre classe concrète ou en appelant getInstance()
. Seule lusine aura des dépendances sur les classes concrètes. Votre classe Main
pourrait servir dusine si vous pensez quune usine dédiée serait exagérée. Par exemple, vous pouvez injecter un ReportGenerator
dans TestExecuter
au lieu dappeler getInstance()
ou new
.
Réponse
Depuis TestExecutor
utilise uniquement ReportGenerator
en interne, vous devriez pouvoir définir une interface pour celui-ci, et vous référer à linterface dans TestScenario
. Alors TestExecutor
dépend de ReportGenerator
, ReportGenerator
dépend de TestScenario
et TestScenario
dépend de ITestExecutor
, qui ne dépend de rien.
Idéalement, vous « Je définirais des interfaces pour toutes vos classes et exprimerais des dépendances à travers elles, mais cest le plus petit changement qui résoudra votre problème.
File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))