Ada masalah OOP klasik dari metode chaining vs metode "single-access-point":
main.getA().getB().getC().transmogrify(x, y)
vs.
main.getA().transmogrifyMyC(x, y)
Yang pertama tampaknya memiliki keuntungan bahwa masing-masing kelas hanya bertanggung jawab untuk satu set operasi yang lebih kecil, dan membuat semuanya lebih modular - menambahkan metode ke C tidak memerlukan upaya apa pun dalam A, B atau C untuk mengeksposnya.
Kelemahannya, tentu saja, adalah enkapsulasi yang lebih lemah , yang dipecahkan oleh kode kedua. Sekarang A memiliki kendali atas setiap metode yang melewatinya, dan dapat mendelegasikannya ke bidangnya jika diinginkan.
Saya menyadari tidak ada solusi tunggal dan tentu saja tergantung pada konteks, tetapi saya benar-benar ingin mendengar beberapa masukan tentang perbedaan penting lainnya antara kedua gaya, dan dalam keadaan apa saya harus memilih keduanya - karena saat ini, ketika saya mencoba untuk merancang beberapa kode, saya merasa seperti saya tidak menggunakan argumen untuk memutuskan satu atau lain cara.
Secara umum saya mencoba untuk menjaga metode chaining sebatas mungkin (berdasarkan Hukum Demeter )
Satu-satunya pengecualian yang saya buat adalah untuk antarmuka yang lancar / pemrograman gaya DSL internal.
Martin Fowler membuat semacam perbedaan yang sama dalam Domain-Bahasa-Khusus tetapi karena alasan pemisahan permintaan perintah pelanggaran yang menyatakan:
Fowler dalam bukunya di halaman 70 mengatakan:
sumber
Saya pikir pertanyaannya adalah, apakah Anda menggunakan abstraksi yang sesuai.
Dalam kasus pertama, kita punya
Di mana utama adalah tipe
IHasGetA
. Pertanyaannya adalah: Apakah abstraksi itu cocok? Jawabannya tidak sepele. Dan dalam hal ini terlihat sedikit aneh, tapi ini contoh teoretis. Tetapi untuk membangun contoh yang berbeda:Seringkali lebih baik daripada
Karena dalam contoh terakhir kedua
this
danmain
tergantung pada jenisv
,w
,x
,y
danz
. Juga, kode tidak benar-benar terlihat jauh lebih baik, jika setiap deklarasi metode memiliki setengah lusin argumen.Seorang pencari lokasi sebenarnya membutuhkan pendekatan pertama. Anda tidak ingin mengakses contoh yang dibuatnya melalui pencari lokasi layanan.
Jadi "menjangkau" melalui suatu objek dapat menciptakan banyak ketergantungan, terlebih lagi jika didasarkan pada properti dari kelas aktual.
Namun menciptakan abstraksi, itu semua tentang menyediakan objek adalah hal yang sama sekali berbeda.
Misalnya, Anda dapat memiliki:
Di mana
main
adalah contoh dariMain
. Jika pengetahuan kelasmain
mengurangi ketergantunganIHasGetA
bukanMain
, Anda akan menemukan kopling menjadi sangat rendah. Kode panggilan bahkan tidak tahu itu sebenarnya memanggil metode terakhir pada objek asli, yang sebenarnya menggambarkan tingkat decoupling.Anda mencapai sepanjang jalan abstraksi singkat dan ortogonal, daripada jauh ke internal implementasi.
sumber
The Law of Demeter , seperti @ Péter Török menunjukkan, menunjukkan "kompak" bentuk.
Selain itu, semakin banyak metode yang Anda sebutkan secara eksplisit dalam kode Anda, semakin banyak kelas yang bergantung pada kelas Anda, meningkatkan masalah pemeliharaan. Dalam contoh Anda, bentuk kompak tergantung pada dua kelas, sedangkan bentuk yang lebih panjang tergantung pada empat kelas. Bentuk yang lebih panjang tidak hanya bertentangan dengan Hukum Demeter; itu juga akan membuat Anda mengubah kode Anda setiap kali Anda mengubah salah satu dari empat metode yang direferensikan (sebagai lawan dua dalam bentuk kompak).
sumber
A
telah akan meledak dengan banyak metode yangA
mungkin ingin didelegasikan pergi. Namun, saya setuju dengan dependensi - yang secara drastis mengurangi jumlah dependensi yang diperlukan dari kode klien.Saya sendiri telah bergulat dengan masalah ini. Kelemahan dengan "mencapai" jauh ke objek yang berbeda adalah bahwa ketika Anda melakukan refactoring Anda akhirnya harus mengubah banyak kode karena ada begitu banyak dependensi. Selain itu, kode Anda menjadi sedikit membengkak dan sulit dibaca.
Di sisi lain, memiliki kelas yang hanya "meneruskan" metode juga berarti overhead harus mendeklarasikan beberapa metode di lebih dari satu tempat.
Solusi yang mengurangi ini dan tepat dalam beberapa kasus adalah memiliki kelas pabrik yang membangun semacam objek fasad dengan menyalin data / objek dari kelas yang sesuai. Dengan begitu Anda dapat mengkodekan objek fasad Anda dan ketika Anda refactor Anda cukup mengubah logika pabrik.
sumber
Saya sering menemukan bahwa logika suatu program lebih mudah untuk grok dengan metode berantai. Bagi saya,
customer.getLastInvoice().itemCount()
cocok dengan otak saya lebih baik daripadacustomer.countLastInvoiceItems()
.Apakah itu sepadan dengan sakit kepala pemeliharaan karena memiliki kopling tambahan terserah Anda. (Saya juga suka fungsi kecil di kelas kecil, jadi saya cenderung berantai. Saya tidak mengatakan itu benar - itu hanya apa yang saya lakukan.)
sumber