Eu tenho três classes que são circulares dependentes uma da outra:
TestExecuter executa solicitações de TestScenario e salva um arquivo de relatório usando a classe ReportGenerator. Tão:
- TestExecuter depende do ReportGenerator para gerar o relatório
- O ReportGenerator depende do TestScenario e dos parâmetros definidos no TestExecuter.
- O TestScenario depende do TestExecuter.
Não consigo descobrir como remover essas dependências.
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: em resposta a uma resposta, mais detalhes sobre a minha 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 */
}
Um exemplo do arquivo xml a ser gerado no caso de um cenário que contém dois testes:
<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))
Respostas:
Tecnicamente, você pode resolver qualquer dependência cíclica usando interfaces, conforme mostrado nas outras respostas. No entanto, recomendo repensar seu design. Eu acho que não é improvável que você possa evitar completamente a necessidade de interfaces adicionais, enquanto seu design se torna ainda mais simples.
Eu acho que não é necessário que um
ReportGenerator
dependa de umTestScenario
diretamente.TestScenario
parece ter duas responsabilidades: é usado para execução de teste e também funciona como um contêiner para os resultados. Isso é uma violação do SRP. Curiosamente, ao resolver essa violação, você também se livrará da dependência cíclica.Portanto, em vez de permitir que o gerador de relatórios pegue dados do cenário de teste, passe os dados explicitamente usando algum objeto de valor. Isso significa, substitua
por algum código como
O método
getReportData
precisa ter um tipo de retorno comoReportData
, um objeto de valor que funcione como um contêiner para os dados serem exibidos no relatório.insertDataToDisplay
é um método que espera um objeto exatamente desse tipo.Dessa maneira,
ReportGenerator
eTestScenario
ambos dependerãoReportData
, o que não depende de mais nada, e as duas primeiras classes não dependem mais uma da outra.Como uma segunda abordagem: para resolver a violação do SRP,
TestScenario
seja responsável por manter os resultados de uma execução de teste, mas não por chamar o executor de teste. Considere reorganizar o código para que o cenário de teste não acesse o executor de teste, mas o executador de teste é iniciado de fora e grava os resultados novamente noTestScenario
objeto. No exemplo que você nos mostrou, isso será possível tornando o acesso ao públicoLinkedList<Test>
internoTestScenario
e movendo oexecute
método deTestScenario
outro lugar, talvez diretamente em umaTestExecuter
, talvez em uma nova classeTestScenarioExecuter
.Dessa forma,
TestExecuter
dependeráTestScenario
eReportGenerator
,ReportGenerator
dependeráTestScenario
, também, masTestScenario
vai depender de nada mais.E, finalmente, uma terceira abordagem:
TestExecuter
tem muitas responsabilidades também. É responsável pela execução de testes e pelo fornecimento de umTestScenario
para aReportGenerator
. Coloque essas duas responsabilidades em duas classes separadas, e sua dependência cíclica desaparecerá novamente.Pode haver mais variantes para abordar seu problema, mas espero que você entenda a idéia geral: seu problema principal são classes com muitas responsabilidades . Resolva esse problema e você se livrará da dependência cíclica automaticamente.
fonte
ReportData
? Você pode editar sua pergunta e explicar um pouco mais detalhadamente o que acontece dentro delesaveReport
.interfaces
.Usando interfaces, você pode resolver a dependência circular.
Design atual:
Projeto proposto:
No projeto proposto, as classes concretas não dependem de outras classes concretas, mas apenas de abstrações (interfaces).
Importante:
Você deve usar o padrão criacional de sua escolha (talvez uma fábrica) para evitar o aperfeiçoamento
new
de qualquer classe concreta dentro de qualquer outra classe ou vocação concretagetInstance()
. Somente a fábrica terá dependências de classes concretas. SuaMain
classe pode servir como fábrica se você acha que uma fábrica dedicada seria um exagero. Por exemplo, você pode injetar umReportGenerator
no emTestExecuter
vez de chamargetInstance()
ounew
.fonte
Como
TestExecutor
apenas usaReportGenerator
internamente, você deve poder definir uma interface para ela e consultar a interface emTestScenario
. EntãoTestExecutor
dependeReportGenerator
,ReportGenerator
dependeTestScenario
eTestScenario
dependeITestExecutor
, o que não depende de nada.Idealmente, você definiria interfaces para todas as suas classes e expressaria dependências através delas, mas essa é a menor alteração que resolverá o seu problema.
fonte