如何解决循环依赖?


33

我有三个相互循环依赖的类:

TestExecuter执行TestScenario的请求,并使用ReportGenerator类保存报告文件。所以:

  • TestExecuter依赖ReportGenerator生成报告
  • ReportGenerator取决于TestScenario和TestExecuter设置的参数。
  • TestScenario取决于TestExecuter。

无法弄清楚如何删除那些依赖项。

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()
    }
}

编辑:回答,有关我的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 */
    }

在包含两个测试的方案的情况下要生成的xml文件的示例:

<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))
shudder

Answers:


35

从技术上讲,您可以使用接口解决任何循环依赖性,如其他答案所示。但是,我建议您重新考虑您的设计。我认为您可以完全避免对其他接口的需求,同时您的设计变得更加简单。

我想a不必直接ReportGenerator依赖TestScenarioTestScenario似乎有两个责任:它用于执行测试,还可以作为结果的容器。这违反了SRP。有趣的是,通过解决该冲突,您也将摆脱循环依赖。

因此,与其让报告生成器从测试场景中获取数据,不如使用某些值对象来显式传递数据。也就是说,更换

   reportGenerator.setTestScenario(ts); 

通过一些类似的代码

reportGenerator.insertDataToDisplay(ts.getReportData()); 

该方法getReportData需要有一个返回类型,例如ReportData,一个值对象,该对象充当要在报表中显示的数据的容器。insertDataToDisplay是一种方法,需要一个完全属于该类型的对象。

这样,ReportGenerator并且TestScenario都将依赖于ReportData,后者不依赖其他任何东西,并且前两个类不再相互依赖。

第二种方法:解决违反SRP的行为,TestScenario负责保存测试执行的结果,而不负责调用测试执行程序。考虑重新组织代码,以便测试场景不能访问测试执行程序,而是从外部启动测试执行程序并将结果写回到TestScenario对象中。在您向我们展示的示例中,这可以通过以下方式实现:访问公共LinkedList<Test>内部空间TestScenario,并将execute方法从TestScenario其他位置移动到其他地方,也许直接移动TestExecuter到a或新类中TestScenarioExecuter

这样,TestExecuter将依赖于TestScenarioReportGeneratorReportGenerator也将依赖于TestScenario,但TestScenario将不依赖其他任何东西。

最后,第三种方法:TestExecuter承担太多责任。它负责执行测试,以及提供一个TestScenarioReportGenerator。将这两个职责放在两个单独的类中,您的循环依赖关系将再次消失。

解决问题的方法可能更多,但是我希望您有一个大致的了解:您的核心问题是职责过多的类。解决该问题,您将自动摆脱循环依赖。


感谢您的回答,实际上我需要TestScenario中的所有信息才能在最后生成报告:(
sabrina2020

@ sabrina2020:是什么阻碍您将所有信息放入ReportData?您可以考虑编辑您的问题,并详细解释发生的情况saveReport
布朗博士

实际上,我的TestScenario包含一个Test列表,并且我希望所有信息都在报告xml文件中,因此在这种情况下,ReportData应该拥有所有信息,我将编辑我的答案以获取更多详细信息,谢谢!
sabrina2020

1
+1:您在时带过我interfaces
乔尔·埃瑟顿

@ sabrina2020:我为答案添加了两种不同的方法,选择最适合您需求的一种。
布朗

8

通过使用接口,您可以解决循环依赖性。

当前设计:

在此处输入图片说明

拟议设计:

在此处输入图片说明

在提出的设计中,具体类不依赖于其他具体类,而仅依赖于抽象(接口)。

重要:

您必须使用选择的创建模式(可能是工厂)来避免new在任何其他具体类或调用中对任何具体类进行置换getInstance()。只有工厂会依赖于具体的类。Main如果您认为一个专门的工厂可能会过大,那么您的班级可以充当工厂。例如,您可以将a ReportGenerator注入TestExecuter而不是调用getInstance()new


3

由于TestExecutor仅在ReportGenerator内部使用,因此您应该能够为其定义一个接口,并参考中的接口TestScenario。然后TestExecutor取决于ReportGeneratorReportGenerator取决于TestScenarioTestScenario取决于ITestExecutor,这与任何内容无关。

理想情况下,您将为所有类定义接口并通过它们表达依赖关系,但这是最小的更改,它将解决您的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.