Làm thế nào để giải quyết phụ thuộc tròn?


33

Tôi có ba lớp là vòng tròn phụ thuộc lẫn nhau:

TestExecuter thực thi các yêu cầu của TestScenario và lưu tệp báo cáo bằng lớp ReportGenerator. Vì thế:

  • TestExecuter phụ thuộc vào ReportGenerator để tạo báo cáo
  • ReportGenerator phụ thuộc vào TestScenario và vào các tham số được đặt từ TestExecuter.
  • TestScenario phụ thuộc vào TestExecuter.

Không thể tìm ra làm thế nào để loại bỏ phụ thuộc.

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: để trả lời câu trả lời, chi tiết hơn về lớp TestScenario của tôi:

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

Một ví dụ về tệp xml sẽ được tạo trong trường hợp kịch bản chứa hai thử nghiệm:

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

Hãy thử xác định các đối tượng của bạn đi ngược lại hỏi bạn cần những gì (đối tượng) để đối tượng trước đó hoạt động - ví dụ:File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))
rùng mình

Câu trả lời:


35

Về mặt kỹ thuật, bạn có thể giải quyết bất kỳ sự phụ thuộc theo chu kỳ bằng cách sử dụng các giao diện, như thể hiện trong các câu trả lời khác. Tuy nhiên, tôi khuyên bạn nên suy nghĩ lại về thiết kế của bạn. Tôi nghĩ rằng không có khả năng bạn có thể tránh được sự cần thiết phải hoàn thành các giao diện bổ sung, trong khi thiết kế của bạn thậm chí còn đơn giản hơn.

Tôi đoán không cần thiết ReportGeneratorphải phụ thuộc TestScenariotrực tiếp vào. TestScenariodường như có hai responsibilites: nó được sử dụng để thực hiện kiểm tra và nó cũng hoạt động như một thùng chứa cho các kết quả. Đây là vi phạm SRP. Thật thú vị, bằng cách giải quyết vi phạm đó, bạn cũng sẽ thoát khỏi sự phụ thuộc theo chu kỳ.

Vì vậy, thay vì để trình tạo báo cáo lấy dữ liệu từ kịch bản thử nghiệm, hãy chuyển dữ liệu một cách rõ ràng bằng cách sử dụng một số đối tượng giá trị. Điều đó có nghĩa là, thay thế

   reportGenerator.setTestScenario(ts); 

bởi một số mã như

reportGenerator.insertDataToDisplay(ts.getReportData()); 

Phương thức getReportDatacần phải có kiểu trả về như ReportData, một đối tượng giá trị hoạt động như một thùng chứa cho dữ liệu sẽ được hiển thị trong báo cáo. insertDataToDisplaylà một phương thức mong đợi một đối tượng chính xác kiểu đó.

Cách này, ReportGeneratorTestScenariocả hai sẽ phụ thuộc vào ReportData, điều này phụ thuộc vào không có gì khác, và hai lớp đầu tiên không phụ thuộc vào nhau nữa.

Như một cách tiếp cận thứ hai: để giải quyết vi phạm SRP, hãy TestScenariochịu trách nhiệm giữ kết quả thực hiện kiểm tra, nhưng không gọi cho người thực hiện kiểm tra. Xem xét để sắp xếp lại mã để không kịch bản kiểm tra truy cập vào trình thực thi kiểm tra, nhưng trình thực thi kiểm tra được bắt đầu từ bên ngoài và ghi lại kết quả vào TestScenariođối tượng. Trong ví dụ bạn đã cho chúng tôi thấy, điều đó sẽ có thể bằng cách truy cập vào LinkedList<Test>bên trong TestScenariocông chúng và bằng cách chuyển executephương thức từ TestScenarionơi khác, có thể trực tiếp vào một TestExecuter, có thể vào một lớp mới TestScenarioExecuter.

Bằng cách đó, TestExecutersẽ phụ thuộc vào TestScenarioReportGenerator, ReportGeneratorcũng sẽ phụ thuộc vào TestScenario, nhưng TestScenariosẽ phụ thuộc vào không có gì khác.

Và cuối cùng, cách tiếp cận thứ ba: TestExecutercũng có quá nhiều trách nhiệm. Nó chịu trách nhiệm thực hiện các bài kiểm tra cũng như cung cấp TestScenariocho a ReportGenerator. Đặt hai trách nhiệm này thành hai lớp riêng biệt và sự phụ thuộc theo chu kỳ của bạn sẽ lại biến mất.

Có thể có nhiều biến thể hơn để tiếp cận vấn đề của bạn, nhưng tôi hy vọng bạn có được ý tưởng chung: vấn đề cốt lõi của bạn là các lớp có quá nhiều trách nhiệm . Giải quyết vấn đề đó và bạn sẽ tự động thoát khỏi sự phụ thuộc theo chu kỳ.


Cảm ơn câu trả lời của bạn, thực sự tôi cần tất cả thông tin trong TestScenario để có thể tạo báo cáo của mình vào cuối :(
sabrina2020

@ sabrina2020: và điều gì cản trở bạn đưa tất cả thông tin đó vào ReportData? Bạn có thể xem xét để chỉnh sửa câu hỏi của mình và giải thích chi tiết hơn một chút về những gì xảy ra bên trong saveReport.
Doc Brown

Trên thực tế, TestScenario của tôi chứa danh sách Kiểm tra và tôi muốn tất cả thông tin trong tệp xml báo cáo để Báo cáo sẽ có tất cả trong trường hợp này, tôi sẽ chỉnh sửa câu trả lời của tôi để biết thêm chi tiết, cảm ơn!
sabrina2020

1
+1: Bạn đã cho tôi tại interfaces.
Joel Etherton

@ sabrina2020: Tôi đã thêm hai cách tiếp cận khác nhau vào câu trả lời của mình, chọn cách tiếp cận phù hợp nhất với nhu cầu của bạn.
Doc Brown

8

Bằng cách sử dụng các giao diện, bạn có thể giải quyết sự phụ thuộc tròn.

Thiết kế hiện hành:

nhập mô tả hình ảnh ở đây

Thiết kế đề xuất:

nhập mô tả hình ảnh ở đây

Trong các lớp bê tông thiết kế được đề xuất, không phụ thuộc vào các lớp cụ thể khác mà chỉ phụ thuộc vào sự trừu tượng (giao diện).

Quan trọng:

Bạn phải sử dụng mô hình sáng tạo của sự lựa chọn của bạn (có thể là một nhà máy) để tránh làm hỏng newbất kỳ lớp cụ thể nào bên trong bất kỳ lớp cụ thể nào khác hoặc gọi getInstance(). Chỉ có nhà máy sẽ có phụ thuộc vào các lớp cụ thể. MainLớp học của bạn có thể phục vụ như một nhà máy nếu bạn nghĩ rằng một nhà máy chuyên dụng sẽ quá mức cần thiết. Ví dụ, bạn có thể tiêm ReportGeneratorvào TestExecuterthay vì gọi getInstance()hoặc new.


3

TestExecutorchỉ sử dụng ReportGeneratornội bộ, bạn sẽ có thể xác định giao diện cho giao diện đó và tham khảo giao diện trong đó TestScenario. Sau đó TestExecutorphụ thuộc vào ReportGenerator, ReportGeneratorphụ thuộc TestScenarioTestScenariophụ thuộc vào ITestExecutor, điều này không phụ thuộc vào bất cứ điều gì.

Lý tưởng nhất là bạn sẽ xác định giao diện cho tất cả các lớp của mình và thể hiện sự phụ thuộc thông qua chúng, nhưng đây là thay đổi nhỏ nhất sẽ giải quyết vấn đề của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.