web-dev-qa-db-ja.com

循環依存を解決する方法は?

互いに循環依存している3つのクラスがあります。

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 */
    }

2つのテストを含むシナリオの場合に生成されるxmlファイルの例:

<testScenario name="scenario1">
   <test name="test1">
     <result>false</result>
   </test>
   <test name="test1">
     <result>true</result>
   </test>
</testScenario >
37
sabrina2020

技術的には、他の回答に示されているように、インターフェースを使用して循環依存関係を解決できます。ただし、デザインを再考することをお勧めします。あなたができる可能性は低いとは思いませんavoid追加のインターフェースが完全に必要である一方で、設計はさらに単純になります。

ReportGeneratorTestScenarioに直接依存している必要はないと思います。 TestScenarioには2つの役割があるようです。テストの実行に使用され、結果のコンテナーとしても機能します。これはSRPの違反です。興味深いことに、その違反を解決することで、循環依存関係も解消されます。

したがって、レポートジェネレーターがテストシナリオからデータを取得するのではなく、値オブジェクトを使用してデータを明示的に渡します。つまり、置き換えます

   reportGenerator.setTestScenario(ts); 

次のようなコードで

reportGenerator.insertDataToDisplay(ts.getReportData()); 

メソッドgetReportDataには、レポートに表示されるデータのコンテナーとして機能する値オブジェクトReportDataのような戻り値の型が必要です。 insertDataToDisplayは、まさにそのタイプのオブジェクトを期待するメソッドです。

このようにして、ReportGeneratorTestScenarioはどちらもReportDataに依存します。これは何にも依存せず、最初の2つのクラスは互いに依存しなくなります。

2番目のアプローチとして、SRP違反を解決するには、TestScenarioがテスト実行の結果を保持する責任を負うようにしますが、テスト実行者を呼び出すことはできません。テストシナリオがテスト実行者にアクセスしないようにコードを再編成することを検討してください。ただし、テスト実行者は外部から開始され、結果をTestScenarioオブジェクトに書き込みます。あなたが私たちに示した例では、TestScenario内でLinkedList<Test>へのアクセスを作成し、executeメソッドをTestScenarioからどこか別の場所で、おそらくTestExecuterに直接、おそらく新しいクラスTestScenarioExecuterに。

このように、TestExecuterTestScenarioReportGeneratorに依存し、ReportGeneratorTestScenarioに依存しますが、TestScenarioは何にも依存しません。

そして最後に、3番目のアプローチ:TestExecuterにも責任が多すぎます。これは、テストの実行とTestScenarioへのReportGeneratorの提供を担当します。これらの2つの責任を2つの別々のクラスに入れると、循環依存関係が再び消えます。

あなたの問題に取り組むためのより多くのバリアントがあるかもしれませんが、私はあなたが一般的な考えを得るのを望みます:あなたのコアの問題は多すぎる責任のクラスです。その問題を解決すると、循環依存関係が自動的に解消されます。

36
Doc Brown

インターフェイスを使用することで、循環依存関係を解決できます。

現在のデザイン:

enter image description here

提案された設計:

enter image description here

提案された設計では、具象クラスは他の具象クラスに依存せず、抽象化(インターフェース)にのみ依存します。

重要:

他のコンクリート内のコンクリートクラスのnewを実行しないように、選択した作成パターンを使用する必要があります(おそらくファクトリ)。クラスまたは呼び出しgetInstance()。ファクトリだけが具象クラスに依存します。専用ファクトリーではやり過ぎだと思われる場合は、Mainクラスをファクトリーとして使用できます。たとえば、getInstance()またはReportGeneratorを呼び出す代わりに、TestExecuternewに注入できます。

9

TestExecutorは内部でReportGeneratorのみを使用するため、そのためのインターフェースを定義し、TestScenarioのインターフェースを参照できるようにする必要があります。次に、TestExecutorReportGeneratorに依存し、ReportGeneratorTestScenarioに依存し、TestScenarioITestExecutorに依存します。 t何かに依存しています。

すべてのクラスのインターフェースを定義し、それらを介して依存関係を表現するのが理想的ですが、これが問題を解決するための最小の変更です。

3
TMN