Hanya karena suatu sistem itu kompleks tidak berarti Anda harus membuatnya rumit . Jika Anda memiliki kelas yang memiliki terlalu banyak dependensi (atau Kolaborator) seperti ini:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... maka itu menjadi terlalu rumit dan Anda tidak benar-benar mengikuti SRP , bukan? Saya berani bertaruh jika Anda menuliskan apa yang MyAwesomeClass
terjadi pada kartu CRC, itu tidak akan muat pada kartu indeks atau Anda harus menulis dengan huruf yang sangat kecil tidak terbaca.
Apa yang Anda miliki di sini adalah bahwa orang-orang Anda hanya mengikuti Prinsip Segregasi Antarmuka sebagai gantinya dan mungkin telah mengambilnya secara ekstrem tetapi itu adalah cerita lain. Anda bisa berargumen bahwa dependensi adalah objek domain (yang terjadi) namun memiliki kelas yang menangani 20 objek domain pada saat yang sama terlalu jauh.
TDD akan memberi Anda indikator yang baik tentang seberapa banyak kelas tidak. Terus terang; jika suatu metode pengujian memiliki kode pengaturan yang membutuhkan waktu lama untuk menulis (bahkan jika Anda melakukan refactor terhadap tes-tes tersebut) maka MyAwesomeClass
kemungkinan Anda memiliki terlalu banyak hal yang harus dilakukan.
Jadi bagaimana Anda memecahkan teka-teki ini? Anda memindahkan tanggung jawab ke kelas lain. Ada beberapa langkah yang dapat Anda ambil pada kelas yang memiliki masalah ini:
- Identifikasi semua tindakan (atau tanggung jawab) yang dilakukan kelas Anda dengan dependensinya.
- Kelompokkan tindakan sesuai dengan ketergantungan yang terkait erat.
- Mendelegasikan kembali! Yakni refactor masing-masing tindakan yang diidentifikasi baik untuk baru atau (lebih penting) kelas lain.
Contoh abstrak tentang tanggung jawab refactoring
Membiarkan C
menjadi kelas yang memiliki beberapa dependensi D1
, D2
, D3
, D4
yang perlu Anda refactor untuk menggunakan lebih sedikit. Ketika kami mengidentifikasi metode apa yang C
memanggil dependensi, kami dapat membuat daftar sederhana tentang itu:
D1
- performA(D2)
,performB()
D2
- performD(D1)
D3
- performE()
D4
- performF(D3)
Melihat daftar kita dapat melihat itu D1
dan D2
terkait satu sama lain karena kelas membutuhkannya bersama-sama. Kita juga bisa melihat D4
kebutuhan itu D3
. Jadi kami memiliki dua pengelompokan:
Group 1
- D1
<->D2
Group 2
- D4
->D3
Pengelompokan adalah indikator bahwa kelas sekarang memiliki dua tanggung jawab.
Group 1
- Satu untuk menangani memanggil dua objek yang saling membutuhkan. Mungkin Anda bisa membiarkan kelas Anda C
menghilangkan kebutuhan menangani kedua dependensi dan membiarkan salah satu dari mereka menangani panggilan itu. Dalam pengelompokan ini, jelas bahwa D1
dapat memiliki referensi D2
.
Group 2
- Tanggung jawab lainnya membutuhkan satu objek untuk memanggil yang lain. Tidak bisa D4
menangani D3
bukan kelas Anda? Maka kita mungkin bisa menghilangkan D3
dari kelas C
dengan membiarkan D4
melakukan panggilan saja.
Jangan mengambil jawaban saya yang sudah jelas seperti contohnya sangat abstrak dan membuat banyak asumsi. Saya cukup yakin ada lebih banyak cara untuk memperbaiki ini, tetapi setidaknya langkah-langkahnya mungkin membantu Anda mendapatkan semacam proses untuk memindahkan tanggung jawab alih-alih membagi kelas.
Sunting:
Di antara komentar @Emmad Karem mengatakan:
"Jika kelas Anda memiliki 20 parameter dalam konstruktor, itu tidak terdengar seperti tim Anda cukup tahu apa SRP itu. Jika Anda memiliki kelas yang hanya melakukan satu hal, bagaimana ia memiliki 20 dependensi?" - Saya pikir jika Anda memiliki kelas Pelanggan, tidak aneh memiliki 20 parameter dalam konstruktor.
Memang benar bahwa objek DAO cenderung memiliki banyak parameter, yang harus Anda atur di konstruktor Anda, dan parameter biasanya adalah tipe sederhana seperti string. Namun dalam contoh Customer
kelas, Anda masih bisa mengelompokkan properti itu di dalam kelas lain untuk membuat semuanya lebih sederhana. Seperti memiliki Address
kelas dengan jalan dan Zipcode
kelas yang berisi kode pos dan akan menangani logika bisnis seperti validasi data juga:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
Hal ini dibahas lebih lanjut dalam posting blog "Jangan, jangan, jangan pernah menggunakan String di Java (atau paling tidak sering)" . Sebagai alternatif menggunakan konstruktor atau metode statis untuk membuat sub objek lebih mudah dibuat Anda bisa menggunakan pola pembangun fluida .