Tengo tres clases que son circulares dependientes entre sí:
TestExecuter ejecuta solicitudes de TestScenario y guarda un archivo de informe usando la clase ReportGenerator. Asi que:
- TestExecuter depende de ReportGenerator para generar el informe
- ReportGenerator depende de TestScenario y de los parámetros establecidos desde TestExecuter.
- TestScenario depende de TestExecuter.
No puedo entender cómo eliminar estas dependencias.
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()
}
}
EDITAR: en respuesta a una respuesta, más detalles sobre mi clase 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 ejemplo del archivo xml que se generará en caso de un escenario que contenga dos pruebas:
<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))
Respuestas:
Técnicamente, puede resolver cualquier dependencia cíclica mediante el uso de interfaces, como se muestra en las otras respuestas. Sin embargo, recomiendo repensar su diseño. Creo que no es improbable que pueda evitar la necesidad de interfaces adicionales por completo, mientras que su diseño se vuelve aún más simple.
Supongo que no es necesario que un
ReportGenerator
dependaTestScenario
directamente de un .TestScenario
parece tener dos responsabilidades: se usa para la ejecución de la prueba y también funciona como un contenedor para los resultados. Esto es una violación del SRP. Curiosamente, al resolver esa violación, también se librará de la dependencia cíclica.Entonces, en lugar de permitir que el generador de informes tome datos del escenario de prueba, pase los datos explícitamente utilizando algún objeto de valor. Eso significa, reemplazar
por algún código como
El método
getReportData
debe tener un tipo de retorno comoReportData
, un objeto de valor que funciona como un contenedor para los datos que se mostrarán en el informe.insertDataToDisplay
es un método que espera un objeto exactamente de ese tipo.De esta manera,
ReportGenerator
yTestScenario
ambos dependeránReportData
, lo que no depende de nada más, y las dos primeras clases ya no dependen entre sí.Como segundo enfoque: para resolver la violación de SRP, seamos
TestScenario
responsables de mantener los resultados de la ejecución de una prueba, pero no de llamar al ejecutor de la prueba. Considere reorganizar el código para que el escenario de prueba no acceda al ejecutante de prueba, pero el ejecutante de prueba se inicia desde el exterior y escribe los resultados nuevamente en elTestScenario
objeto. En el ejemplo que nos mostró, eso será posible haciendo que el acceso alLinkedList<Test>
interior delTestScenario
público, y moviendo elexecute
método deTestScenario
otro lugar, tal vez directamente a unaTestExecuter
, tal vez a una nueva claseTestScenarioExecuter
.De esa manera,
TestExecuter
dependeráTestScenario
yReportGenerator
,ReportGenerator
dependeráTestScenario
, también, peroTestScenario
dependerá de nada más.Y finalmente, un tercer enfoque:
TestExecuter
también tiene demasiadas responsabilidades. Es responsable de ejecutar pruebas y de proporcionar aTestScenario
a aReportGenerator
. Ponga estas dos responsabilidades en dos clases separadas, y su dependencia cíclica se desvanecerá nuevamente.Puede haber más variantes para abordar su problema, pero espero que comprenda la idea general: su problema principal son las clases con demasiadas responsabilidades . Resuelva ese problema y se librará de la dependencia cíclica automáticamente.
fuente
ReportData
? Puede considerar editar su pregunta y explicar un poco más detallado lo que sucede dentro desaveReport
.interfaces
.Mediante el uso de interfaces, puede resolver la dependencia circular.
Diseño actual:
Diseño propuesto:
En el diseño propuesto, las clases concretas no dependen de otras clases concretas sino solo de abstracciones (interfaces).
Importante:
Debe usar el patrón de creación que elija (tal vez una fábrica) para evitar la ejecución
new
de cualquier clase concreta dentro de cualquier otra clase concreta o llamadagetInstance()
. Solo la fábrica tendrá dependencias de clases concretas. SuMain
clase podría servir como fábrica si cree que una fábrica dedicada sería exagerada. Por ejemplo, puede inyectar unReportGenerator
enTestExecuter
lugar de llamar agetInstance()
onew
.fuente
Como
TestExecutor
solo se usaReportGenerator
internamente, debe poder definir una interfaz para ella y consultar la interfaz enTestScenario
. LuegoTestExecutor
dependeReportGenerator
,ReportGenerator
dependeTestScenario
yTestScenario
depende deITestExecutor
, lo que no depende de nada.Lo ideal sería definir interfaces para todas sus clases y expresar dependencias a través de ellas, pero este es el cambio más pequeño que resolverá su problema.
fuente