Wie löse ich zirkuläre Abhängigkeiten?
On Januar 1, 2021 by adminIch habe drei Klassen, die kreisförmig voneinander abhängig sind:
TestExecuter führt Anforderungen von TestScenario aus und speichert eine Berichtsdatei mit der ReportGenerator-Klasse . Also:
- TestExecuter hängt von ReportGenerator ab, um den Bericht zu generieren.
- ReportGenerator hängt von TestScenario und den von TestExecuter festgelegten Parametern ab.
- TestScenario hängt von TestExecuter ab.
Kann nicht herausfinden, wie diese Abhängigkeiten entfernt werden.
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() } }
BEARBEITEN: als Antwort auf eine Antwort, weitere Details zu meiner TestScenario-Klasse:
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 */ }
Ein Beispiel für die XML-Datei, die im Falle eines Szenarios mit zwei Tests generiert werden soll:
<testScenario name="scenario1"> <test name="test1"> <result>false</result> </test> <test name="test1"> <result>true</result> </test> </testScenario >
Kommentare
- Versuchen Sie, Ihr Objekt zu identifizieren s gehen rückwärts und fragen , was (Objekt) Sie benötigen, damit das vorherige funktioniert – zum Beispiel:
File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))
- @ sabrina2020, seitdem A ist in B wie B in A, sollte ‚ nicht ein und dasselbe sein?
Antwort
Technisch gesehen können Sie jede zyklische Abhängigkeit mithilfe von Schnittstellen auflösen, wie in den anderen Antworten gezeigt. Ich empfehle jedoch, Ihr Design zu überdenken. Ich denke, es ist nicht unwahrscheinlich, dass Sie die Notwendigkeit zusätzlicher Schnittstellen vollständig vermeiden können, während Ihr Design noch einfacher wird. P. >
Ich denke, es ist nicht erforderlich, dass eine ReportGenerator
direkt von einer TestScenario
abhängt. TestScenario
scheint zwei Verantwortlichkeiten zu haben: Es wird für die Testausführung verwendet und fungiert auch als Container für die Ergebnisse. Dies ist eine Verletzung der SRP. Interessanterweise wird durch die Behebung dieser Verletzung auch die zyklische Abhängigkeit beseitigt.
Anstatt den Berichtsgenerator Daten aus dem Testszenario abrufen zu lassen, übergeben Sie die Daten explizit mithilfe eines Wertobjekts. Das heißt, ersetzen Sie
reportGenerator.setTestScenario(ts);
durch einen Code wie
reportGenerator.insertDataToDisplay(ts.getReportData());
Die Methode getReportData
muss einen Rückgabetyp wie ReportData
haben, ein Wertobjekt, das als Container für die im Bericht anzuzeigenden Daten fungiert. insertDataToDisplay
ist eine Methode, die ein Objekt genau dieses Typs erwartet.
Auf diese Weise ReportGenerator
und TestScenario
hängt beide von ReportData
ab, was von nichts anderem abhängt, und die ersten beiden Klassen hängen nicht mehr voneinander ab.
Als zweiten Ansatz: Um die SRP-Verletzung zu beheben, ist TestScenario
dafür verantwortlich, die Ergebnisse einer Testausführung zu speichern, nicht jedoch den Testausführer aufzurufen. Ziehen Sie in Betracht, den Code neu zu organisieren, damit nicht das Testszenario auf den Test-Executer zugreift, sondern der Test-Executer von außen gestartet wird und die Ergebnisse zurück in das Objekt TestScenario
schreibt. In dem Beispiel, das Sie uns gezeigt haben, ist dies möglich, indem Sie den Zugriff auf LinkedList<Test>
innerhalb von TestScenario
öffentlich machen und das execute
Methode von TestScenario
zu einem anderen Ort, möglicherweise direkt in eine TestExecuter
, möglicherweise in eine neue Klasse TestScenarioExecuter
.
Auf diese Weise hängt TestExecuter
von TestScenario
ab und ReportGenerator
, ReportGenerator
hängt auch von TestScenario
ab, aber TestScenario
hängt von nichts anderem ab.
Und schließlich ein dritter Ansatz: TestExecuter
hat auch zu viele Verantwortlichkeiten. Es ist verantwortlich für die Ausführung von Tests sowie für die Bereitstellung einer TestScenario
für eine ReportGenerator
. Wenn Sie diese beiden Verantwortlichkeiten in zwei separate Klassen einteilen, verschwindet Ihre zyklische Abhängigkeit wieder.
Es gibt möglicherweise mehr Varianten, um Ihr Problem anzugehen, aber ich hoffe, Sie haben die allgemeine Idee: Ihr Kernproblem sind Klassen mit zu viele Verantwortlichkeiten . Wenn Sie dieses Problem lösen, wird die zyklische Abhängigkeit automatisch beseitigt.
Kommentare
- Vielen Dank für Ihre Antwort. Eigentlich benötige ich alle Informationen in TestScenario um meinen Bericht am Ende erstellen zu können 🙁
- @ sabrina2020: und was hindert Sie daran, all diese Informationen in
ReportData
zu speichern?Sie können Ihre Frage bearbeiten und etwas detaillierter erklären, was insaveReport
passiert. - Eigentlich enthält mein TestScenario eine Liste von Tests, und ich möchte alle Informationen in einer Berichts-XML-Datei, damit die ReportData in diesem Fall alles enthalten. Ich werde meine Antwort für weitere Details bearbeiten, danke!
- +1: Sie hatten mich unter
interfaces
. - @ sabrina2020: Ich habe meiner Antwort zwei verschiedene Ansätze hinzugefügt. Wählen Sie den aus, der Ihren Anforderungen am besten entspricht.
Antwort
Mithilfe von Schnittstellen können Sie die zirkuläre Abhängigkeit lösen.
Aktuelles Design:
Vorgeschlagenes Design:
Im vorgeschlagenen Entwurfsbeton Klassen hängen nicht von anderen konkreten Klassen ab, sondern nur von Abstraktionen (Schnittstellen).
Wichtig:
Sie müssen das Kreationsmuster Ihrer Wahl (möglicherweise eine Fabrik) verwenden, um ein Durchführen zu vermeiden new
aller konkreten Klassen innerhalb einer anderen konkreten Klasse oder Aufruf von getInstance()
. Nur die Fabrik wird von konkreten Klassen abhängig sein. Ihre Main
-Klasse könnte als Fabrik dienen, wenn Sie der Meinung sind, dass eine dedizierte Fabrik übertrieben wäre. Sie können beispielsweise eine ReportGenerator
in TestExecuter
einfügen, anstatt getInstance()
oder new
.
Antwort
Seit TestExecutor
Verwendet ReportGenerator
nur intern. Sie sollten in der Lage sein, eine Schnittstelle dafür zu definieren und auf die Schnittstelle in TestScenario
zu verweisen. Dann hängt TestExecutor
von ReportGenerator
ab, ReportGenerator
hängt von TestScenario
und TestScenario
hängen von ITestExecutor
ab, was von nichts abhängt.
Idealerweise Sie „Ich würde Schnittstellen für alle Ihre Klassen definieren und Abhängigkeiten durch sie ausdrücken, aber dies ist die kleinste Änderung, die Ihr Problem lösen wird.
Schreibe einen Kommentar