Ich 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. So:
- TestExecuter ist von ReportGenerator abhängig, um den Bericht zu generieren
- ReportGenerator hängt von TestScenario und den in TestExecuter festgelegten Parametern ab.
- TestScenario ist abhängig von TestExecuter.
Kann nicht herausfinden, wie man diese Abhängigkeiten entfernt.
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 bei einem Szenario mit zwei Tests generiert werden soll:
<testScenario name="scenario1">
<test name="test1">
<result>false</result>
</test>
<test name="test1">
<result>true</result>
</test>
</testScenario >
File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))
Antworten:
Technisch gesehen können Sie zyklische Abhängigkeiten mithilfe von Schnittstellen auflösen, wie in den anderen Antworten gezeigt. Ich empfehle jedoch, Ihr Design zu überdenken. Ich denke , es ist nicht unwahrscheinlich ist , können Sie vermeiden die Notwendigkeit für zusätzliche Schnittstellen vollständig, während Ihr Design wird noch einfacher.
Ich denke, es ist nicht notwendig, dass man sich direkt
ReportGenerator
auf aTestScenario
verlässt.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 des SRP. Interessanterweise werden Sie durch Beheben dieser Verletzung auch die zyklische Abhängigkeit beseitigen.Anstatt den Berichtsgenerator Daten aus dem Testszenario abrufen zu lassen, übergeben Sie die Daten explizit mithilfe eines Wertobjekts. Das heißt, ersetzen
von einem Code wie
Die Methode
getReportData
muss einen Rückgabetyp wieReportData
ein Wertobjekt haben, 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 hängen
ReportGenerator
undTestScenario
werden beide davon abReportData
, 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,
TestScenario
müssen Sie dafür verantwortlich sein, die Ergebnisse einer Testausführung zu speichern, aber nicht den Testausführer aufzurufen. Überlegen Sie, den Code so zu reorganisieren, dass nicht das Testszenario auf den Test-Executer zugreift, sondern der Test-Executer von außen gestartet wird und die Ergebnisse zurück in dasTestScenario
Objekt schreibt . In dem Beispiel, das Sie uns gezeigt haben, wird dies möglich sein, indem Sie den Zugriff auf dasLinkedList<Test>
Innere derTestScenario
Öffentlichkeit ermöglichen und dieexecute
Methode von einem OrtTestScenario
an einen anderen verschieben, vielleicht direkt in eineTestExecuter
, vielleicht in eine neue KlasseTestScenarioExecuter
.Auf diese Weise
TestExecuter
wird davon abhängen ,TestScenario
undReportGenerator
,ReportGenerator
hängt davon ab ,TestScenario
auch, aberTestScenario
auf nichts anderes ab.Und zum Schluss noch ein dritter Ansatz:
TestExecuter
Hat auch zu viele Verantwortlichkeiten. Es ist verantwortlich für die Durchführung von Tests sowie für die Bereitstellung von aTestScenario
zu aReportGenerator
. Teilen Sie diese beiden Verantwortlichkeiten in zwei separate Klassen ein, und Ihre zyklische Abhängigkeit wird wieder verschwinden.Möglicherweise gibt es weitere Varianten, um Ihr Problem anzugehen, aber ich hoffe, Sie haben eine allgemeine Vorstellung davon: Ihr Kernproblem sind Klassen mit zu vielen Verantwortlichkeiten . Lösen Sie dieses Problem und Sie werden die zyklische Abhängigkeit automatisch los.
quelle
ReportData
? Sie können in Betracht ziehen, Ihre Frage zu bearbeiten und etwas detaillierter zu erklären, was in der Frage passiertsaveReport
.interfaces
.Durch die Verwendung von Schnittstellen können Sie die zirkuläre Abhängigkeit lösen.
Aktuelles Design:
Vorgeschlagenes Design:
In dem vorgeschlagenen Entwurf hängen konkrete Klassen nicht von anderen konkreten Klassen ab, sondern nur von Abstraktionen (Schnittstellen).
Wichtig:
Sie müssen das Schöpfungsmuster Ihrer Wahl (möglicherweise eine Fabrik) verwenden, um zu vermeiden,
new
dass konkrete Klassen innerhalb einer anderen konkreten Klasse oder eines Aufrufs ausgeführt werdengetInstance()
. Nur die Fabrik wird Abhängigkeiten von konkreten Klassen haben. IhreMain
Klasse könnte als Fabrik dienen, wenn Sie glauben, eine dedizierte Fabrik wäre übertrieben. Zum Beispiel können Sie einReportGenerator
in einfügen,TestExecuter
anstattgetInstance()
oder aufzurufennew
.quelle
Da
TestExecutor
nurReportGenerator
intern verwendet, sollten Sie in der Lage sein, eine Schnittstelle dafür zu definieren und auf die Schnittstelle in zu verweisenTestScenario
. DannTestExecutor
kommt es darauf anReportGenerator
,ReportGenerator
kommt darauf anTestScenario
undTestScenario
kommt darauf anITestExecutor
, was von nichts abhängt.Idealerweise definieren Sie Schnittstellen für alle Ihre Klassen und drücken Abhängigkeiten durch diese aus. Dies ist jedoch die kleinste Änderung, die Ihr Problem löst.
quelle