Dairesel bağımlılık nasıl çözülür?


33

Birbirine dairesel bağlı üç sınıfım var:

TestExecuter, TestScenario'nun isteklerini yerine getirir ve ReportGenerator sınıfını kullanarak bir rapor dosyasını saklar. Yani:

  • TestExecuter, raporu oluşturmak için ReportGenerator'e bağlıdır
  • ReportGenerator, TestScenario'ya ve TestExecuter'dan ayarlanan parametrelere bağlıdır.
  • TestScenario, TestExecuter'a bağlıdır.

Bağırsak bağımlılıklarının nasıl giderileceğini çözemiyorum.

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: bir cevaba cevaben, TestScenario sınıfım hakkında daha fazla detay:

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

İki test içeren bir senaryo olması durumunda oluşturulacak xml dosyası örneği:

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

Nesnelerinizin geriye doğru gitmesini , önceki birinin çalışması için neye ihtiyacınız olduğunu sorarak tanımlamayı deneyin - örneğin:File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))
titreme

Yanıtlar:


35

Teknik olarak, herhangi bir döngüsel bağımlılığı, diğer cevaplarda gösterildiği gibi arayüzleri kullanarak çözebilirsiniz. Ancak, tasarımınızı yeniden düşünmenizi tavsiye ederim. Tasarımınız daha da basitleşirken, tamamen ilave arayüzlere ihtiyaç duyulmasından kaçınmanın mümkün olmadığını düşünüyorum .

Sanırım ReportGeneratorbir TestScenariodoğrudan bağlı için gerekli değildir . TestScenarioiki sorumluluğu var gibi gözüküyor: test uygulaması için kullanılıyor ve sonuçlar için bir kap olarak da çalışıyor. Bu, SRP'nin ihlalidir. İlginç bir şekilde, bu ihlali çözerek, döngüsel bağımlılıktan da kurtulacaksınız.

Bu nedenle, rapor oluşturucunun test senaryosundan veri toplamasına izin vermek yerine, bir değer nesnesi kullanarak verileri açıkça iletin. Bu, yerine

   reportGenerator.setTestScenario(ts); 

gibi bazı kodlarla

reportGenerator.insertDataToDisplay(ts.getReportData()); 

Yöntemin , raporda görüntülenecek verilerin kapsayıcısı olarak çalışan bir değer nesnesi getReportDatagibi bir dönüş türüne sahip ReportDataolması gerekir. insertDataToDisplaytam da bu tür bir nesneyi bekleyen bir yöntemdir.

Bu şekilde, ReportGeneratorve TestScenarioher ikisi de ReportData, başka hiçbir şeye bağlı olmayana bağlı olacak ve ilk iki sınıf artık birbirine bağlı değil.

İkinci bir yaklaşım olarak: SRP ihlalini çözmek, TestScenariotest yürütme sonuçlarını tutmaktan sorumludur, ancak test yürütücüsünü aramaktan değil. Kodu yeniden düzenleyin, böylece test senaryosu test yürütücüsüne erişemez, ancak test yürütücüsü dışarıdan başlatılır ve sonuçları TestScenarionesneye geri yazar . Bize gösterdiğiniz örnekte, bu LinkedList<Test>, TestScenariohalkın içine erişimi sağlayarak ve executeyöntemi TestScenariobaşka bir yere, belki doğrudan bir TestExecuter, belki de yeni bir sınıfa taşıyarak mümkün olacak TestScenarioExecuter.

Bu şekilde, TestExecuterbuna bağlı olacak TestScenariove ReportGeneratorde ReportGeneratorbuna bağlı olacak TestScenario, fakat TestScenariobaşka hiçbir şeye bağlı olmayacak.

Ve son olarak, üçüncü bir yaklaşım: TestExecuterçok fazla sorumluluğu var. Bu testleri çalıştırmak için yanısıra sağlamaktan sorumludur TestScenarioa ReportGenerator. Bu iki sorumluluğu iki ayrı sınıfa koyun; döngüsel bağımlılığınız yeniden kaybolacak.

Probleminize yaklaşmak için daha fazla değişken olabilir, ancak umarım genel bir fikir edinebilirsiniz: temel probleminiz çok fazla sorumluluk sahibi sınıflardır . Bu sorunu çözün ve otomatik olarak döngüsel bağımlılıktan kurtulacaksınız.


Cevabınız için teşekkürler, aslında TestScenario'daki tüm bilgilere ihtiyacım var. Sonunda raporumu hazırlayabiliyorum :(
sabrina2020

@ sabrina2020: ve tüm bu bilgileri vermenizi engelleyen şey ReportDatanedir? Sorunuzu düzenlemeyi ve içinde ne olduğunu biraz daha ayrıntılı olarak açıklamayı düşünebilirsiniz saveReport.
Doktor Brown

Aslında TestScenario'mda bir Test listesi var ve bir raporun xml dosyasındaki tüm bilgileri istiyorum, böylece ReportData'nın bu durumda olması gerekir, daha fazla ayrıntı için cevabımı düzenlerim, teşekkürler!
sabrina2020,

1
+1: Beni aldın interfaces.
Joel Etherton

@ sabrina2020: Cevabınıza iki farklı yaklaşım ekledim, ihtiyaçlarınıza en uygun olanı seçtim.
Doktor Brown

8

Arabirimleri kullanarak dairesel bağımlılığı çözebilirsiniz.

Mevcut Tasarım:

görüntü tanımını buraya girin

Önerilen tasarım:

görüntü tanımını buraya girin

Önerilen tasarımda somut sınıflar, diğer somut sınıflara değil, yalnızca soyutlamalara (arayüzler) bağlıdır.

Önemli:

Başka herhangi bir somut sınıfın içinde herhangi bir somut sınıfın iç içe geçmesini veya çağrı yapmasını önlemek için istediğiniz yaratım modelini (belki bir fabrika) kullanmanız gerekir . Sadece fabrikanın somut sınıflara bağımlılığı olacaktır. Sizin size özel bir fabrika boğucu geldiği düşünüyorsanız sınıf fabrikası olarak hizmet edebilir. Örneğin, aramak yerine veya içine bir enjekte edebilirsiniz .newgetInstance()MainReportGeneratorTestExecutergetInstance()new


3

Yana TestExecutorsadece kullanan ReportGeneratoriçten, bunun için bir arabirim tanımlamak ve arayüzde başvurmak gerekir TestScenario. Sonra TestExecutorbağlıdır ReportGenerator, ReportGeneratorbağlı TestScenariove TestScenariobağlı ITestExecutorşeye bağlı değildir, hangi.

İdeal olarak, tüm sınıflarınız için arayüzler tanımlayacak ve aralarındaki bağımlılıkları ifade edebileceksiniz, ancak bu probleminizi çözecek en küçük değişiklik.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.