Melewati fungsi ke fungsi lain sebagai parameter, praktik buruk?

40

Kami telah dalam proses mengubah cara aplikasi AS3 kami berbicara ke ujung belakang kami dan kami sedang dalam proses menerapkan sistem REST untuk menggantikan yang lama.

Sayangnya pengembang yang memulai pekerjaan sekarang cuti sakit jangka panjang dan sudah diserahkan kepada saya. Saya telah bekerja dengannya sekitar seminggu yang lalu dan saya mengerti sistemnya, tetapi ada satu hal yang membuat saya khawatir. Tampaknya ada banyak fungsi lewat ke fungsi. Misalnya kelas kami yang melakukan panggilan ke server kami mengambil fungsi yang kemudian akan memanggil dan meneruskan objek ketika proses selesai dan kesalahan telah ditangani dll.

Ini memberi saya "perasaan buruk" di mana saya merasa itu adalah praktik yang mengerikan dan saya dapat memikirkan beberapa alasan mengapa tetapi saya ingin konfirmasi sebelum saya mengusulkan kembali bekerja ke sistem. Saya bertanya-tanya apakah ada yang punya pengalaman dengan masalah ini?

Elliot Blackburn
sumber
22
Kenapa kamu merasa seperti itu? Apakah Anda punya pengalaman dengan pemrograman fungsional? (Saya kira tidak, tetapi Anda harus melihatnya)
Phoshi
8
Kalau begitu anggap ini pelajaran! Apa yang diajarkan akademisi dan apa yang sebenarnya bermanfaat seringkali secara mengejutkan sedikit tumpang tindih. Ada seluruh dunia teknik pemrograman di luar sana yang tidak sesuai dengan dogma semacam itu.
Phoshi
32
Melewati fungsi ke fungsi lain adalah konsep mendasar sehingga ketika suatu bahasa tidak mendukungnya, orang-orang akan keluar dari jalan mereka untuk membuat "objek" sepele yang tujuan utamanya adalah untuk memuat fungsi yang dipertanyakan sebagai pengganti sementara.
Doval
36
Kembali pada hari saya, kami memanggil fungsi melewati ini "Callbacks"
Mike

Jawaban:

84

Itu bukan masalah.

Ini adalah teknik yang dikenal. Ini adalah fungsi urutan yang lebih tinggi (fungsi yang mengambil fungsi sebagai parameter).

Semacam ini fungsi juga merupakan blok bangunan dasar dalam pemrograman fungsional dan digunakan secara luas dalam bahasa fungsional seperti Haskell .

Fungsi-fungsi seperti itu tidak buruk atau bagus - jika Anda belum pernah menemukan gagasan dan teknik yang awalnya sulit untuk dipahami, tetapi mereka bisa sangat kuat dan merupakan alat yang baik untuk dimiliki di sabuk alat Anda.

Oded
sumber
Terima kasih untuk itu, saya tidak punya pengalaman nyata dengan pemrograman fungsional jadi saya akan memeriksanya. Itu hanya tidak duduk dengan baik karena dari sedikit pengetahuan saya, ketika Anda mendeklarasikan sesuatu sebagai fungsi pribadi maka hanya kelas yang harus memiliki akses ke sana dan yang publik harus dipanggil dengan mengacu pada objek. Saya akan memeriksanya dengan benar dan saya berharap saya akan menghargainya sedikit lebih banyak!
Elliot Blackburn
3
Membaca tentang kelanjutan , kelanjutan gaya lewat , monad (pemrograman fungsional) juga harus bermanfaat.
Basile Starynkevitch
8
@BlueHat Anda mendeklarasikan sesuatu sebagai pribadi jika Anda ingin mengontrol bagaimana penggunaannya, jika penggunaan itu sebagai panggilan balik untuk fungsi tertentu maka itu baik
ratchet freak
5
Persis. Menandai sebagai pribadi berarti Anda tidak ingin orang lain masuk dan mengaksesnya dengan nama kapan saja mereka mau. Melewati fungsi pribadi ke orang lain karena Anda secara khusus ingin mereka memanggilnya dengan cara mereka mendokumentasikan parameter itu, benar-benar baik-baik saja. Tidak perlu berbeda dengan meneruskan nilai bidang pribadi sebagai parameter ke beberapa fungsi lain, yang mungkin tidak cocok dengan Anda :-)
Steve Jessop
2
Perlu dicatat bahwa jika Anda menemukan diri Anda menulis kelas konkret dengan fungsi sebagai parameter konstruktor Anda mungkin salah melakukannya .
Gusdor
30

Mereka tidak hanya digunakan untuk pemrograman fungsional. Mereka juga bisa dikenal sebagai callback :

Panggilan balik adalah bagian dari kode yang dapat dieksekusi yang dilewatkan sebagai argumen ke kode lain, yang diharapkan untuk memanggil kembali (mengeksekusi) argumen pada waktu yang tepat. Doa dapat langsung seperti dalam callback sinkron atau mungkin terjadi di lain waktu, seperti dalam callback tidak sinkron.

Pikirkan kode asinkron selama satu detik. Anda meneruskan fungsi yang, misalnya, mengirim data ke pengguna. Hanya ketika kode telah selesai Anda menjalankan fungsi ini dengan hasil dari respon, yang kemudian digunakan fungsi untuk mengirim data kembali ke pengguna. Ini perubahan pola pikir.

Saya menulis perpustakaan yang mengambil data Torrent dari kotak seedbox Anda. Anda menggunakan loop acara non-pemblokiran untuk mengeksekusi pustaka ini dan mendapatkan data, lalu mengembalikannya kepada pengguna (katakanlah, dalam konteks websocket). Bayangkan Anda memiliki 5 orang yang terhubung dalam loop acara ini, dan salah satu permintaan untuk mendapatkan kios data torrent seseorang. Itu akan memblokir seluruh loop. Jadi Anda perlu berpikir secara tidak sinkron dan menggunakan panggilan balik - loop tetap berjalan dan "memberikan data kembali ke pengguna" hanya berjalan ketika fungsi telah selesai dieksekusi, jadi tidak ada menunggu untuk itu. Api dan lupakan.

James
sumber
1
+1 Untuk menggunakan terminologi panggilan balik. Saya menemukan istilah ini lebih sering daripada "fungsi tingkat tinggi".
Rodney Schuler
Saya suka jawaban ini, karena OP sepertinya mendeskripsikan callback, secara khusus, dan bukan hanya fungsi tingkat tinggi secara umum: "Misalnya kelas kita yang membuat panggilan ke server kita mengambil fungsi yang kemudian akan memanggil dan meneruskan objek ketika proses selesai dan kesalahan telah ditangani dll. "
Ajedi32
11

Ini bukan hal yang buruk. Padahal, itu adalah hal yang sangat baik.

Melewati fungsi menjadi fungsi sangat penting untuk pemrograman sehingga kami menemukan fungsi lambda sebagai singkatan. Sebagai contoh, seseorang dapat menggunakan lambdas dengan algoritma C ++ untuk menulis kode yang sangat ringkas namun ekspresif yang memungkinkan algoritma generik kemampuan untuk menggunakan variabel lokal dan negara lain untuk melakukan hal-hal seperti mencari dan menyortir.

Pustaka berorientasi objek mungkin juga memiliki panggilan balik yang pada dasarnya adalah antarmuka yang menetapkan sejumlah kecil fungsi (idealnya satu, tetapi tidak selalu). Seseorang kemudian dapat membuat kelas sederhana yang mengimplementasikan antarmuka itu dan meneruskan objek kelas itu melalui suatu fungsi. Itu adalah landasan pemrograman event-driven , di mana kode kerangka kerja tingkat (mungkin bahkan di utas lainnya) perlu memanggil menjadi objek untuk mengubah keadaan sebagai respons terhadap tindakan pengguna. Antarmuka ActionListener Java adalah contoh yang bagus untuk ini.

Secara teknis, functor C ++ juga merupakan jenis objek callback yang memanfaatkan gula sintaksis operator()(),, untuk melakukan hal yang sama.

Akhirnya, ada pointer fungsi C-style yang seharusnya hanya digunakan dalam C. Saya tidak akan menjelaskan secara rinci, saya hanya menyebutkannya untuk kelengkapan. Abstraksi lain yang disebutkan di atas jauh lebih unggul dan harus digunakan dalam bahasa yang memilikinya.

Yang lain menyebutkan pemrograman fungsional dan bagaimana fungsi lewat sangat alami dalam bahasa tersebut. Lambdas dan panggilan balik adalah cara bahasa prosedural dan OOP meniru itu, dan mereka sangat kuat dan berguna.


sumber
Juga, dalam delegasi C # dan lambdas keduanya digunakan secara luas.
Evil Dog Pie
6

Seperti yang sudah dikatakan, ini bukan praktik yang buruk. Ini hanyalah cara memisahkan dan memisahkan tanggung jawab. Misalnya, dalam OOP Anda akan melakukan sesuatu seperti ini:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

Metode generik mendelegasikan tugas tertentu - yang tidak tahu apa-apa tentang - ke objek lain yang mengimplementasikan antarmuka. Metode generik hanya mengetahui antarmuka ini. Dalam kasus Anda, antarmuka ini akan menjadi fungsi untuk dipanggil.

Philipp Murry
sumber
1
Idiom ini hanya membungkus fungsi dalam sebuah antarmuka, menyelesaikan sesuatu yang sangat mirip dengan melewatkan fungsi mentah.
Ya itu, saya hanya ingin menunjukkan bahwa ini bukan praktik yang tidak biasa.
Philipp Murry
3

Secara umum, tidak ada yang salah dengan melewatkan fungsi ke fungsi lain. Jika Anda melakukan panggilan tidak sinkron dan ingin melakukan sesuatu dengan hasilnya, maka Anda akan memerlukan semacam mekanisme panggilan balik.

Meskipun demikian, ada beberapa kelemahan potensial dari panggilan balik sederhana:

  • Melakukan serangkaian panggilan dapat membutuhkan bersarangnya panggilan balik yang mendalam.
  • Penanganan kesalahan dapat membutuhkan pengulangan untuk setiap panggilan dalam urutan panggilan.
  • Mengkoordinasikan beberapa panggilan adalah canggung, seperti membuat beberapa panggilan sekaligus dan kemudian melakukan sesuatu setelah semuanya selesai.
  • Tidak ada cara umum untuk membatalkan satu set panggilan.

Dengan layanan web sederhana cara Anda melakukannya berfungsi dengan baik, tetapi menjadi canggung jika Anda membutuhkan urutan panggilan yang lebih kompleks. Ada beberapa alternatif. Dengan JavaScript misalnya, ada pergeseran menuju penggunaan janji ( Apa yang hebat tentang janji javascript ).

Mereka masih melibatkan fungsi lewat fungsi lain tetapi panggilan tidak sinkron mengembalikan nilai yang mengambil panggilan balik daripada mengambil panggilan balik secara langsung. Ini memberi lebih banyak fleksibilitas untuk menyusun panggilan-panggilan ini bersama-sama. Sesuatu seperti ini dapat diimplementasikan dengan cukup mudah di ActionScript.

fgb
sumber
Saya juga akan menambahkan bahwa penggunaan callback yang tidak perlu dapat membuat lebih sulit untuk mengikuti alur eksekusi bagi siapa pun yang membaca kode. Panggilan eksplisit lebih mudah dipahami daripada panggilan balik yang ditetapkan di bagian kode yang jauh. (Breakpoints to the rescue!) Meskipun demikian, ini adalah bagaimana peristiwa terjadi di browser dan sistem berbasis acara lainnya; maka kita perlu memahami strategi acara emitor untuk mengetahui kapan dan dalam urutan apa penangan acara akan dipanggil.
joeytwiddle