Metode chaining adalah praktik metode objek mengembalikan objek itu sendiri agar hasilnya dipanggil untuk metode lain. Seperti ini:
participant.addSchedule(events[1]).addSchedule(events[2]).setStatus('attending').save()
Ini tampaknya dianggap praktik yang baik, karena menghasilkan kode yang dapat dibaca, atau "antarmuka yang lancar". Namun, bagi saya itu tampaknya memecah notasi pemanggilan objek yang tersirat oleh orientasi objek itu sendiri - kode yang dihasilkan tidak mewakili tindakan yang dilakukan untuk hasil dari metode sebelumnya, yang adalah bagaimana kode berorientasi objek umumnya diharapkan bekerja:
participant.getSchedule('monday').saveTo('monnday.file')
Perbedaan ini berhasil membuat dua makna berbeda untuk notasi titik "memanggil objek yang dihasilkan": Dalam konteks chaining, contoh di atas akan dibaca sebagai menyimpan objek peserta , meskipun contoh sebenarnya dimaksudkan untuk menyimpan jadwal. objek diterima oleh getSchedule.
Saya mengerti bahwa perbedaannya di sini adalah apakah metode yang dipanggil harus diharapkan untuk mengembalikan sesuatu atau tidak (dalam hal ini akan mengembalikan objek yang disebut itu sendiri untuk dirantai). Tetapi kedua kasus ini tidak dapat dibedakan dari notasi itu sendiri, hanya dari semantik metode yang dipanggil. Ketika metode rantai tidak digunakan, saya selalu bisa tahu bahwa pemanggilan metode beroperasi pada sesuatu yang terkait dengan hasil panggilan sebelumnya - dengan rantai, asumsi ini rusak, dan saya harus secara semantik memproses seluruh rantai untuk memahami apa objek sebenarnya menjadi disebut benar-benar. Sebagai contoh:
participant.attend(event).setNotifications('silent').getSocialStream('twitter').postStatus('Joining '+event.name).follow(event.getSocialId('twitter'))
Di sana dua panggilan metode terakhir merujuk pada hasil getSocialStream, sedangkan yang sebelumnya merujuk ke peserta. Mungkin praktik yang buruk untuk benar-benar menulis rantai di mana konteksnya berubah (kan?), Tetapi bahkan kemudian Anda harus terus-menerus memeriksa apakah rantai-titik yang terlihat serupa sebenarnya tetap dalam konteks yang sama, atau hanya bekerja pada hasilnya .
Bagi saya tampaknya sementara metode chaining dangkal menghasilkan kode yang dapat dibaca, membebani arti dari notasi titik hanya menghasilkan lebih banyak kebingungan. Karena saya tidak menganggap diri saya seorang guru pemrograman, saya berasumsi kesalahannya adalah milik saya. Jadi: Apa yang saya lewatkan? Apakah saya mengerti metode chaining yang salah? Apakah ada beberapa kasus di mana metode chaining sangat baik, atau beberapa di mana itu sangat buruk?
Sidenote: Saya mengerti pertanyaan ini bisa dibaca sebagai pernyataan pendapat yang disembunyikan sebagai pertanyaan. Namun, itu tidak - Saya benar-benar ingin memahami mengapa merantai dianggap praktik yang baik, dan di mana saya salah dalam berpikir itu merusak notasi berorientasi objek yang melekat.
sumber
.
itu akan mengabaikan nilai pengembalian mehtod dan selalu memanggil metode berantai apa pun menggunakan objek yang sama.Jawaban:
Saya setuju bahwa ini subjektif. Sebagian besar saya menghindari metode chaining, tetapi baru-baru ini saya juga menemukan kasus di mana itu adalah hal yang tepat - saya memiliki metode yang menerima sekitar 10 parameter, dan membutuhkan lebih banyak, tetapi untuk sebagian besar waktu Anda hanya perlu menentukan beberapa. Dengan mengabaikan ini menjadi sangat rumit sangat cepat. Alih-alih, saya memilih pendekatan chaining:
Pendekatan metode rantai adalah opsional, tetapi itu membuat penulisan kode lebih mudah (terutama dengan IntelliSense). Pikiran Anda bahwa ini adalah satu kasus yang terisolasi, dan bukan praktik umum dalam kode saya.
Intinya adalah - dalam 99% kasus Anda mungkin dapat melakukannya dengan baik atau bahkan lebih baik tanpa metode chaining. Tapi ada 1% di mana ini adalah pendekatan terbaik.
sumber
P = MyObject.GetParamsObj().SomeParameter(asdasd).SomeOtherParameter(asdasd); Obj = MyObject.Start(); MyObject.Execute(P);
. Anda memiliki keuntungan untuk dapat menggunakan kembali objek parameter ini di panggilan lain, yang merupakan nilai tambah!PizzaBuilder.AddSauce().AddDough().AddTopping()
lebih banyak referensi di sinilist.add(someItem)
, saya membaca bahwa sebagai "kode ini menambahkansomeItem
kelist
objek". jadi ketika saya membacaPizzaBuilder.AddSauce()
, saya membaca ini secara alami sebagai "kode ini menambahkan saus kePizzaBuilder
objek". Dengan kata lain, saya melihat orkestra (yang melakukan penambahan) sebagai metode di mana kodelist.add(someItem)
atauPizzaBuilder.addSauce()
tertanam. Tapi saya pikir contoh PizzaBuilder sedikit buatan. Contoh AndaMyObject.SpecifySomeParameter(asdasd)
berfungsi dengan baik untuk saya.Hanya 2 sen saya;
Chaining metode membuat debugging menjadi rumit: - Anda tidak bisa meletakkan breakpoint di titik yang ringkas sehingga Anda dapat menjeda program tepat di tempat yang Anda inginkan - Jika salah satu metode ini membuat pengecualian, dan Anda mendapatkan nomor baris, Anda tidak tahu metode mana dalam "rantai" yang menyebabkan masalah.
Saya pikir itu praktik yang baik untuk selalu menulis baris yang sangat pendek dan ringkas. Setiap baris seharusnya hanya membuat satu panggilan metode. Pilih lebih banyak garis daripada garis yang lebih panjang.
EDIT: komentar menyebutkan bahwa metode chaining dan line-breaking terpisah. Itu benar. Tergantung pada debugger, mungkin atau tidak mungkin untuk menempatkan break point di tengah pernyataan. Bahkan jika Anda bisa, menggunakan baris terpisah dengan variabel perantara memberi Anda lebih banyak fleksibilitas dan sejumlah besar nilai yang dapat Anda periksa di jendela Tonton yang membantu proses debugging.
sumber
Secara pribadi, saya lebih suka metode merantai yang hanya bertindak pada objek asli, misalnya mengatur beberapa properti atau memanggil metode tipe utilitas.
Saya tidak menggunakannya ketika satu atau lebih metode dirantai akan mengembalikan objek selain foo dalam contoh saya. Meskipun secara sintaksis Anda dapat membuat rantai apa pun selama Anda menggunakan API yang benar untuk objek dalam rantai tersebut, mengubah objek IMHO membuat hal-hal menjadi kurang mudah dibaca dan dapat benar-benar membingungkan jika API untuk objek yang berbeda memiliki kesamaan. Jika Anda melakukan beberapa benar-benar umum metode panggilan di akhir (
.toString()
,.print()
, apa pun) yang objek yang Anda akhirnya bertindak atas? Seseorang yang dengan santai membaca kode mungkin tidak mengetahui bahwa itu akan menjadi objek yang dikembalikan secara implisit dalam rantai daripada referensi asli.Merantai objek yang berbeda juga dapat menyebabkan kesalahan nol yang tidak terduga. Dalam contoh saya, dengan asumsi bahwa foo valid, semua pemanggilan metode "aman" (misalnya, valid untuk foo). Dalam contoh OP:
... tidak ada jaminan (sebagai pengembang luar melihat kode) bahwa getSchedule benar-benar akan mengembalikan objek jadwal yang valid dan tidak nol. Juga, men-debug gaya kode ini seringkali jauh lebih sulit karena banyak IDE tidak akan mengevaluasi pemanggilan metode pada waktu debug sebagai objek yang dapat Anda periksa. IMO, kapan saja Anda mungkin perlu objek untuk memeriksa untuk keperluan debugging, saya lebih suka memilikinya dalam variabel eksplisit.
sumber
Participant
tidak memiliki yang validSchedule
makagetSchedule
metode dirancang untuk mengembalikan suatuMaybe(of Schedule)
jenis dansaveTo
metode dirancang untuk menerima suatuMaybe
jenis.Martin Fowler memiliki diskusi yang baik di sini:
sumber
Menurut pendapat saya, metode chaining agak sedikit baru. Tentu, itu terlihat keren tapi saya tidak melihat keuntungan nyata di dalamnya.
Bagaimana:
lebih baik dari:
Pengecualian mungkin ketika addObject () mengembalikan objek baru, dalam hal ini kode yang tidak dirantai mungkin sedikit lebih rumit seperti:
sumber
Ini berbahaya karena Anda mungkin bergantung pada lebih banyak objek dari yang diharapkan, seperti saat panggilan Anda mengembalikan instance kelas lain:
Saya akan memberi contoh:
foodStore adalah objek yang terdiri dari banyak toko makanan yang Anda miliki. foodstore.getLocalStore () mengembalikan objek yang menyimpan informasi di toko terdekat ke parameter. getPriceforProduct (apa saja) adalah metode objek itu.
Jadi ketika Anda memanggil foodStore.getLocalStore (parameter) .getPriceforProduct (apa saja)
Anda tidak hanya bergantung pada FoodStore seperti Anda, tetapi juga pada LocalStore.
Jika getPriceforProduct (apa saja) pernah berubah, Anda perlu mengubah tidak hanya FoodStore tetapi juga kelas yang disebut metode berantai.
Anda harus selalu mengusahakan kopling longgar di antara kelas-kelas.
Yang sedang berkata, saya pribadi suka rantai mereka ketika pemrograman Ruby.
sumber
Banyak yang menggunakan metode chaining sebagai bentuk kenyamanan daripada memikirkan masalah keterbacaan. Metode chaining dapat diterima jika melibatkan melakukan tindakan yang sama pada objek yang sama - tetapi hanya jika itu benar-benar meningkatkan keterbacaan, dan tidak hanya untuk menulis lebih sedikit kode.
Sayangnya banyak menggunakan metode chaining sesuai contoh yang diberikan dalam pertanyaan. Sementara mereka dapat tetap dilakukan dibaca, mereka sayangnya menyebabkan kopling tinggi antara beberapa kelas, sehingga tidak diinginkan.
sumber
Ini sepertinya agak subyektif.
Metode chaining bukanlah sesuatu yang inheren buruk atau bagus.
Keterbacaan adalah hal yang paling penting.
(Juga pertimbangkan bahwa memiliki banyak metode yang dirantai akan membuat segalanya sangat rapuh jika sesuatu berubah)
sumber
Manfaat Chaining
yaitu, di mana saya suka menggunakannya
Satu manfaat dari rantai yang tidak saya lihat disebutkan adalah kemampuan untuk menggunakannya selama inisiasi variabel, atau ketika meneruskan objek baru ke suatu metode, tidak yakin apakah ini praktik yang buruk atau tidak.
Saya tahu ini adalah contoh buat tapi katakan Anda memiliki kelas berikut
Dan katakan Anda tidak memiliki akses ke kelas dasar, atau Katakan nilai default dinamis, berdasarkan waktu, dll. Ya Anda bisa instantiate, kemudian ubah nilainya tetapi itu bisa menjadi rumit, terutama jika Anda hanya melewati nilai-nilai untuk suatu metode:
Tapi bukankah ini lebih mudah dibaca:
Atau, bagaimana dengan anggota kelas?
vs.
Ini adalah cara saya menggunakan chaining, dan biasanya metode saya hanya untuk konfigurasi, jadi panjangnya hanya 2 baris, tentukan nilainya
Return Me
. Bagi kami itu telah membersihkan garis besar yang sangat sulit untuk dibaca dan memahami kode menjadi satu baris yang berbunyi seperti kalimat. sesuatu sepertiVs Sesuatu seperti
Kerugian Of Chaining
yaitu, di mana saya tidak suka menggunakannya
Saya tidak menggunakan rantai ketika ada banyak parameter untuk dilewatkan ke rutinitas, terutama karena garis menjadi sangat panjang, dan seperti yang disebutkan OP itu bisa membingungkan ketika Anda memanggil rutin ke kelas lain untuk lolos ke salah satu metode chaining.
Ada juga kekhawatiran bahwa suatu rutin akan mengembalikan data yang tidak valid, sejauh ini saya hanya menggunakan rantai ketika saya mengembalikan contoh yang sama dipanggil. Seperti yang ditunjukkan jika Anda rantai antar kelas Anda membuat debug lebih sulit (mana yang dikembalikan nol?) Dan dapat meningkatkan ketergantungan kopling antar kelas.
Kesimpulan
Seperti segala sesuatu dalam hidup, dan pemrograman, Chaining tidak baik, atau buruk jika Anda dapat menghindari yang buruk maka chaining bisa menjadi manfaat besar.
Saya mencoba mengikuti aturan ini.
sumber
Metode chaining dapat memungkinkan untuk merancang DSL canggih di Jawa secara langsung. Intinya, Anda dapat memodelkan setidaknya jenis aturan DSL ini:
Aturan-aturan ini dapat diimplementasikan menggunakan antarmuka ini
Dengan aturan sederhana ini, Anda dapat mengimplementasikan DSL kompleks seperti SQL langsung di Jawa, seperti yang dilakukan oleh jOOQ , perpustakaan yang telah saya buat. Lihat contoh SQL agak rumit yang diambil dari blog saya di sini:
Contoh bagus lainnya adalah jRTF , sebuah DSL kecil yang dirancang untuk membuat dokumen RTF langsung di Jawa. Sebuah contoh:
sumber
Metode chaining mungkin hanya merupakan hal baru untuk sebagian besar kasus, tetapi saya pikir metode ini ada di tempatnya. Salah satu contoh dapat ditemukan dalam penggunaan Rekaman Aktif CodeIgniter :
Itu terlihat jauh lebih bersih (dan menurut saya lebih masuk akal) daripada:
Ini benar-benar subjektif; Setiap orang memiliki pendapatnya sendiri.
sumber
Saya pikir kesalahan utama adalah berpikir ini adalah pendekatan berorientasi objek secara umum padahal sebenarnya itu lebih merupakan pendekatan pemrograman fungsional daripada yang lain.
Alasan utama saya menggunakannya adalah untuk keterbacaan dan mencegah kode saya dibanjiri oleh variabel.
Saya tidak begitu mengerti apa yang orang lain bicarakan ketika mereka mengatakan itu merusak keterbacaan. Ini adalah salah satu bentuk pemrograman yang paling ringkas dan kohesif yang pernah saya gunakan.
Ini juga:
convertTextToVoice.LoadText ("source.txt"). ConvertToVoice ("destination.wav");
adalah bagaimana saya biasanya menggunakannya. Menggunakannya untuk rantai x jumlah parameter bukan bagaimana saya biasanya menggunakannya. Jika saya ingin memasukkan x jumlah parameter dalam pemanggilan metode, saya akan menggunakan sintaks params :
public void foo (objek params [] item)
Dan melemparkan objek berdasarkan tipe atau hanya menggunakan array tipe data atau koleksi tergantung pada kasus penggunaan Anda.
sumber
Saya setuju, karenanya saya mengubah cara antarmuka yang lancar diimplementasikan di perpustakaan saya.
Sebelum:
Setelah:
Dalam implementasi "sebelum" fungsi memodifikasi objek dan berakhir pada
return this
. Saya mengubah implementasi untuk mengembalikan objek baru dengan tipe yang sama .Alasan saya untuk perubahan ini :
Nilai kembali tidak ada hubungannya dengan fungsi, itu murni ada untuk mendukung bagian rantai, seharusnya fungsi batal menurut OOP.
Metode chaining di pustaka sistem juga menerapkannya seperti itu (seperti LINQ atau string):
Objek asli tetap utuh, memungkinkan pengguna API untuk memutuskan apa yang harus dilakukan dengannya. Ini memungkinkan untuk:
Sebuah implementasi copy juga dapat digunakan untuk membangun objek:
Di mana
setBackground(color)
fungsi mengubah instance dan mengembalikan apa-apa (seperti yang seharusnya) .Perilaku fungsi lebih mudah diprediksi (Lihat poin 1 & 2).
Menggunakan nama variabel pendek juga dapat mengurangi kekacauan kode, tanpa memaksa api pada model.
Kesimpulan:
Menurut saya antarmuka yang lancar yang menggunakan
return this
implementasi adalah salah.sumber
return this
dikelola atau sebaliknya. Ini adalah pendapat saya bahwa ia memperoleh api yang lancar dengan cara yang "tidak alami". (Ditambahkan alasan 6: untuk menunjukkan alternatif yang tidak lancar, yang tidak memiliki fungsi overhead / tambahan)Poin yang benar-benar terlewatkan di sini, adalah bahwa metode chaining memungkinkan untuk KERING . Ini merupakan dukungan efektif untuk "with" (yang tidak diterapkan dengan baik dalam beberapa bahasa).
Ini penting karena alasan yang sama KERING selalu penting; jika A ternyata merupakan kesalahan, dan operasi ini perlu dilakukan pada B, Anda hanya perlu memperbarui di 1 tempat, bukan 3.
Secara pragmatis, keuntungannya kecil dalam hal ini. Namun, sedikit mengetik, sedikit lebih kuat (KERING), saya akan menerimanya.
sumber
Saya biasanya benci metode chaining karena saya pikir itu memperburuk keterbacaan. Kekompakan sering dikacaukan dengan keterbacaan, tetapi istilahnya tidak sama. Jika Anda melakukan semuanya dalam satu pernyataan maka itu kompak, tetapi itu kurang dapat dibaca (sulit untuk diikuti) sebagian besar kali daripada melakukannya dalam beberapa pernyataan. Seperti yang Anda perhatikan kecuali Anda tidak dapat menjamin bahwa nilai pengembalian metode yang digunakan adalah sama, maka metode rantai akan menjadi sumber kebingungan.
1.)
vs.
2.)
vs.
3.)
vs.
Seperti yang Anda lihat Anda menang hampir tidak ada, karena Anda harus menambahkan jeda baris pada pernyataan tunggal Anda untuk membuatnya lebih mudah dibaca dan Anda harus menambahkan lekukan untuk memperjelas bahwa Anda berbicara tentang objek yang berbeda. Yah jika saya ingin menggunakan bahasa berbasis identifikasi, maka saya akan belajar Python daripada melakukan ini, belum lagi bahwa sebagian besar IDE akan menghapus lekukan dengan memformat kode secara otomatis.
Saya pikir satu-satunya tempat di mana jenis chaining dapat bermanfaat adalah aliran pipa di CLI atau BERGABUNG dengan beberapa pertanyaan bersama dalam SQL. Keduanya memiliki harga untuk beberapa pernyataan. Tetapi jika Anda ingin menyelesaikan masalah yang rumit Anda akan berakhir bahkan dengan mereka yang membayar harga dan menulis kode dalam beberapa pernyataan menggunakan variabel atau menulis skrip bash dan prosedur atau tampilan yang tersimpan.
Pada interpretasi KERING: "Hindari pengulangan pengetahuan (bukan pengulangan teks)." dan "Ketik yang lebih sedikit, bahkan jangan ulangi teks.", yang pertama artinya benar-benar berarti, tetapi yang kedua adalah kesalahpahaman yang umum karena banyak orang tidak dapat memahami omong kosong yang terlalu rumit seperti "Setiap pengetahuan harus memiliki satu, tidak ambigu, representasi otoritatif dalam suatu sistem ". Yang kedua adalah kekompakan di semua biaya, yang pecah dalam skenario ini, karena memperburuk keterbacaan. Interpretasi pertama dipecah oleh DDD ketika Anda menyalin kode antara konteks yang dibatasi, karena kopling longgar lebih penting dalam skenario itu.
sumber
Yang baik:
Keburukan:
Umum:
Pendekatan yang baik adalah tidak menggunakan rantai secara umum sampai situasi muncul atau modul tertentu akan sangat cocok untuk itu.
Dalam beberapa kasus, rantai dapat merusak keterbacaan yang cukup parah terutama ketika menimbang di poin 1 dan 2.
Pada accasation dapat disalahgunakan, seperti bukannya pendekatan lain (melewati array misalnya) atau metode campuran dengan cara yang aneh (parent.setSomething (). GetChild (). SetSomething (). SetPeteru (). GetParent () .setSomething ()).
sumber
Jawaban yang Diusulkan
Kelemahan terbesar dari rantai adalah sulit bagi pembaca untuk memahami bagaimana setiap metode memengaruhi objek asli, jika ya, dan jenis apa yang dikembalikan setiap metode.
Beberapa pertanyaan:
Debugging, dalam sebagian besar bahasa, memang bisa lebih sulit dengan rantai. Bahkan jika setiap langkah dalam rantai berada pada jalurnya sendiri (jenis yang mengalahkan tujuan rantai), akan sulit untuk memeriksa nilai yang dikembalikan setelah setiap langkah, khususnya untuk metode yang tidak bermutasi.
Waktu kompilasi bisa lebih lambat tergantung pada bahasa dan kompiler, karena ekspresi bisa jauh lebih rumit untuk diselesaikan.
Saya percaya bahwa seperti halnya segala sesuatu, rantai adalah solusi yang baik yang dapat berguna dalam beberapa skenario. Ini harus digunakan dengan hati-hati, memahami implikasi, dan membatasi jumlah elemen rantai menjadi beberapa.
sumber
Dalam bahasa yang diketik (yang kurang
auto
atau setara) ini menyimpan pelaksana dari harus menyatakan jenis hasil antara.Untuk rantai yang lebih lama Anda mungkin berurusan dengan beberapa jenis perantara yang berbeda, Anda harus mendeklarasikan masing-masing.
Saya percaya bahwa pendekatan ini benar-benar dikembangkan di Jawa di mana a) semua pemanggilan fungsi adalah pemanggilan fungsi anggota, dan b) diperlukan jenis eksplisit. Tentu saja ada trade-off di sini dalam hal, kehilangan beberapa saksi mata, tetapi dalam beberapa situasi beberapa orang merasa berharga sementara.
sumber