Saya memiliki tiga kelas yang saling bergantung satu sama lain:
TestExecuter menjalankan permintaan dari TestScenario dan menyimpan file laporan menggunakan kelas ReportGenerator. Begitu:
- TestExecuter bergantung pada ReportGenerator untuk menghasilkan laporan
- ReportGenerator tergantung pada TestScenario dan pada parameter yang ditetapkan dari TestExecuter.
- TestScenario tergantung pada TestExecuter.
Tidak dapat menemukan cara menghapus dependensi mereka.
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: sebagai tanggapan atas jawaban, detail lebih lanjut tentang kelas TestScenario saya:
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 */
}
Contoh file xml yang akan dihasilkan jika ada skenario yang berisi dua tes:
<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))
Jawaban:
Secara teknis, Anda dapat menyelesaikan ketergantungan siklik dengan menggunakan antarmuka, seperti yang ditunjukkan pada jawaban lainnya. Namun, saya sarankan untuk memikirkan kembali desain Anda. Saya pikir bukan tidak mungkin Anda dapat menghindari kebutuhan untuk antarmuka tambahan sepenuhnya, sementara desain Anda menjadi lebih sederhana.
Saya kira itu tidak perlu untuk
ReportGenerator
bergantung pada secaraTestScenario
langsung.TestScenario
tampaknya memiliki dua tanggung jawab: digunakan untuk pelaksanaan tes, dan berfungsi juga sebagai wadah untuk hasilnya. Ini merupakan pelanggaran terhadap SRP. Menariknya, dengan menyelesaikan pelanggaran itu, Anda akan menyingkirkan ketergantungan siklik juga.Jadi alih-alih membiarkan pembuat laporan mengambil data dari skenario pengujian, berikan data secara eksplisit dengan menggunakan beberapa objek nilai. Itu artinya, ganti
oleh beberapa kode seperti
Metode
getReportData
harus memiliki tipe pengembalian sepertiReportData
, objek nilai yang berfungsi sebagai wadah untuk data yang akan ditampilkan dalam laporan.insertDataToDisplay
adalah metode yang mengharapkan objek dengan tipe seperti itu.Dengan cara ini,
ReportGenerator
danTestScenario
keduanya akan bergantungReportData
, yang tidak bergantung pada yang lain, dan dua kelas pertama tidak lagi saling bergantung.Sebagai pendekatan kedua: untuk menyelesaikan pelanggaran SRP, biarkan
TestScenario
bertanggung jawab untuk memegang hasil dari eksekusi tes, tetapi tidak untuk memanggil pelaksana tes. Pertimbangkan untuk menata ulang kode sehingga skenario pengujian tidak mengakses pelaksana tes, tetapi pelaksana pengujian dimulai dari luar dan menulis hasilnya kembali keTestScenario
objek. Dalam contoh yang Anda tunjukkan kepada kami, itu akan dimungkinkan dengan membuat akses keLinkedList<Test>
dalamTestScenario
publik, dan dengan memindahkanexecute
metode dariTestScenario
ke tempat lain, mungkin langsung keTestExecuter
, mungkin ke kelas baruTestScenarioExecuter
.Dengan cara itu,
TestExecuter
akan bergantung padaTestScenario
danReportGenerator
,ReportGenerator
akan bergantung padaTestScenario
, juga, tetapi tidakTestScenario
akan bergantung pada yang lain.Dan akhirnya, pendekatan ketiga:
TestExecuter
memiliki terlalu banyak tanggung jawab juga. Ini bertanggung jawab untuk melaksanakan tes serta untuk menyediakanTestScenario
aReportGenerator
. Masukkan dua tanggung jawab ini ke dalam dua kelas yang terpisah, dan ketergantungan siklik Anda akan hilang lagi.Mungkin ada lebih banyak varian untuk mendekati masalah Anda, tetapi saya harap Anda mendapatkan ide umum: masalah inti Anda adalah kelas dengan tanggung jawab terlalu banyak . Selesaikan masalah itu, dan Anda akan menyingkirkan ketergantungan siklik secara otomatis.
sumber
ReportData
? Anda dapat mempertimbangkan untuk mengedit pertanyaan Anda dan menjelaskan sedikit lebih rinci apa yang terjadi di dalamnyasaveReport
.interfaces
.Dengan menggunakan antarmuka Anda dapat menyelesaikan ketergantungan sirkular.
Desain saat ini:
Desain yang diusulkan:
Dalam desain yang diusulkan, kelas beton tidak bergantung pada kelas beton lain tetapi hanya pada abstraksi (antarmuka).
Penting:
Anda harus menggunakan pola kreasi pilihan Anda (mungkin pabrik) untuk menghindari perfusi
new
kelas beton apa pun di dalam kelas atau panggilan beton lainnyagetInstance()
. Hanya pabrik yang akan memiliki ketergantungan pada kelas beton.Main
Kelas Anda dapat berfungsi sebagai pabrik jika Anda berpikir pabrik yang berdedikasi akan berlebihan. Misalnya Anda dapat menyuntikkanReportGenerator
ke dalamTestExecuter
alih-alih menelepongetInstance()
ataunew
.sumber
Karena
TestExecutor
hanya menggunakan secaraReportGenerator
internal, Anda harus dapat mendefinisikan antarmuka untuk itu, dan merujuk ke antarmuka diTestScenario
. MakaTestExecutor
tergantung padaReportGenerator
,ReportGenerator
tergantung padaTestScenario
, danTestScenario
tergantung padaITestExecutor
, yang tidak tergantung pada apa pun.Idealnya Anda akan mendefinisikan antarmuka untuk semua kelas Anda dan mengekspresikan dependensi melalui mereka, tetapi ini adalah perubahan terkecil yang akan menyelesaikan masalah Anda.
sumber