วิธีการแก้ปัญหาการพึ่งพาแบบวงกลม?


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))
ตัวสั่น

คำตอบ:


35

ในทางเทคนิคคุณสามารถแก้ไขการพึ่งพาแบบวนรอบได้โดยใช้ส่วนต่อประสานดังที่แสดงในคำตอบอื่น ๆ อย่างไรก็ตามฉันแนะนำให้คิดใหม่เกี่ยวกับการออกแบบของคุณ ฉันคิดว่าไม่น่าเป็นไปได้ที่คุณจะสามารถหลีกเลี่ยงความต้องการอินเทอร์เฟซเพิ่มเติมได้อย่างสมบูรณ์ในขณะที่การออกแบบของคุณจะง่ายขึ้น

ฉันเดาว่ามันไม่จำเป็นสำหรับ a ที่ReportGeneratorจะต้องพึ่งพาTestScenarioโดยตรงTestScenarioดูเหมือนว่ามีสองความรับผิดชอบ: มันถูกใช้สำหรับการทดสอบการทำงานและมันยังทำงานเป็นที่เก็บสำหรับผลลัพธ์ นี่เป็นการละเมิด SRP ที่น่าสนใจโดยการแก้ไขการละเมิดนั้นคุณจะกำจัดการพึ่งพาแบบวนรอบเช่นกัน

ดังนั้นแทนที่จะปล่อยให้ตัวสร้างรายงานดึงข้อมูลจากสถานการณ์การทดสอบให้ส่งข้อมูลอย่างชัดเจนโดยใช้วัตถุค่าบางอย่าง นั่นหมายถึงแทนที่

   reportGenerator.setTestScenario(ts); 

โดยรหัสบางอย่างเช่น

reportGenerator.insertDataToDisplay(ts.getReportData()); 

วิธีการgetReportDataจะต้องมีประเภทส่งคืนเช่นReportDataวัตถุค่าที่ทำงานเป็นภาชนะสำหรับข้อมูลที่จะแสดงในรายงาน insertDataToDisplayเป็นวิธีการที่คาดว่าวัตถุประเภทนั้นอย่างแน่นอน

ด้วยวิธีนี้ReportGeneratorและTestScenarioทั้งสองจะขึ้นอยู่กับReportDataซึ่งขึ้นอยู่กับสิ่งอื่นใดและสองคลาสแรกไม่ได้พึ่งพาซึ่งกันและกันอีกต่อไป

วิธีที่สอง: การแก้ไขการละเมิด SRP ให้TestScenarioรับผิดชอบในการเก็บผลลัพธ์ของการดำเนินการทดสอบ แต่ไม่ใช่การโทรหาผู้ดำเนินการทดสอบ พิจารณาจัดระเบียบรหัสใหม่เพื่อไม่ให้สถานการณ์จำลองการทดสอบเข้าถึงผู้ดำเนินการทดสอบ แต่ผู้ดำเนินการทดสอบนั้นเริ่มต้นจากภายนอกและเขียนผลลัพธ์กลับเข้าไปในTestScenarioวัตถุ ในตัวอย่างที่คุณแสดงให้เราเห็นว่าจะเป็นไปได้โดยการทำให้การเข้าถึงLinkedList<Test>ด้านในของTestScenarioสาธารณะและโดยการย้ายexecuteวิธีการจากTestScenarioไปที่อื่นอาจจะโดยตรงลงอาจจะเป็นคลาสใหม่TestExecuterTestScenarioExecuter

วิธีการที่TestExecuterจะขึ้นอยู่กับTestScenarioและReportGenerator, ReportGeneratorจะขึ้นอยู่กับTestScenarioเกินไป แต่TestScenarioจะขึ้นอยู่กับอะไรอย่างอื่น

และในที่สุดแนวทางที่สาม: TestExecuterมีความรับผิดชอบมากเกินไป มันเป็นความรับผิดชอบสำหรับการดำเนินการทดสอบเช่นเดียวกับการให้บริการไปยังTestScenario ReportGeneratorใส่ความรับผิดชอบทั้งสองนี้ไว้ในสองชั้นที่แยกจากกันและการพึ่งพาแบบวนรอบของคุณจะหายไปอีกครั้ง

อาจจะมีสายพันธุ์ที่จะเข้าถึงปัญหาของคุณ แต่ฉันหวังว่าคุณจะได้รับความคิดทั่วไป: ปัญหาหลักของคุณมีชั้นเรียนที่มีความรับผิดชอบมากเกินไป แก้ไขปัญหานั้นและคุณจะกำจัดการพึ่งพาวงจรโดยอัตโนมัติ


ขอบคุณสำหรับคำตอบของคุณจริง ๆ แล้วฉันต้องการข้อมูลทั้งหมดใน TestScenario เพื่อสร้างรายงานของฉันในตอนท้าย :(
sabrina2020

@ sabrina2020: และอะไรขัดขวางคุณจากการใส่ข้อมูลทั้งหมดเข้าไปReportData? คุณอาจจะพิจารณาเพื่อแก้ไขคำถามของคุณและอธิบายรายละเอียดนิด ๆ หน่อย ๆ saveReportสิ่งที่เกิดขึ้นภายในของ
Doc Brown

ที่จริง TestScenario ของฉันมีรายการทดสอบและฉันต้องการข้อมูลทั้งหมดในไฟล์รายงาน xml ดังนั้น ReportData จะมีทุกอย่างในกรณีนี้ฉันจะแก้ไขคำตอบของฉันสำหรับรายละเอียดเพิ่มเติมขอบคุณ!
sabrina2020

1
1: interfacesคุณมีฉันที่
Joel Etherton

@ sabrina2020: ฉันเพิ่มสองวิธีที่แตกต่างกันในคำตอบของฉันเลือกวิธีที่เหมาะสมกับความต้องการของคุณที่สุด
Doc Brown

8

โดยใช้อินเทอร์เฟซคุณสามารถแก้ปัญหาการอ้างอิงแบบวงกลม

การออกแบบในปัจจุบัน:

ป้อนคำอธิบายรูปภาพที่นี่

การออกแบบที่เสนอ:

ป้อนคำอธิบายรูปภาพที่นี่

ในชั้นเรียนการออกแบบที่นำเสนอไม่ได้ขึ้นอยู่กับชั้นเรียนคอนกรีตอื่น ๆ แต่เฉพาะใน abstractions (อินเทอร์เฟซ)

สิ่งสำคัญ:

คุณต้องใช้รูปแบบความคิดสร้างสรรค์ที่คุณเลือก (อาจจะเป็นโรงงาน) เพื่อหลีกเลี่ยงnewการสร้างคลาสคอนกรีตใด ๆ ในคลาสคอนกรีตหรือการโทรgetInstance()อื่น ๆ มีเพียงโรงงานเท่านั้นที่ต้องพึ่งพาชั้นเรียนคอนกรีต Mainชั้นเรียนของคุณสามารถทำหน้าที่เป็นโรงงานได้ถ้าคุณคิดว่าโรงงานที่ทุ่มเทจะมากเกินไป ตัวอย่างเช่นคุณสามารถฉีดReportGeneratorเข้าไปTestExecuterแทนการโทรหรือgetInstance()new


3

ตั้งแต่TestExecutorใช้เฉพาะภายในคุณควรจะสามารถที่จะกำหนดอินเตอร์เฟซสำหรับมันและอ้างถึงอินเตอร์เฟซในReportGenerator TestScenarioจากนั้นTestExecutorขึ้นอยู่กับReportGenerator, ReportGeneratorขึ้นอยู่กับTestScenario, และTestScenarioขึ้นอยู่กับITestExecutor, ซึ่งไม่ได้ขึ้นอยู่กับอะไรเลย

เป็นการดีที่คุณจะกำหนดอินเทอร์เฟซสำหรับคลาสทั้งหมดของคุณและแสดงการอ้างอิงผ่านพวกเขา แต่นี่เป็นการเปลี่ยนแปลงที่เล็กที่สุดที่จะแก้ปัญหาของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.