Inilah masalah pemrograman / bahasa yang ingin saya dengarkan.
Kami telah mengembangkan konvensi yang diikuti oleh sebagian besar programmer yang bukan merupakan bagian dari sintaks bahasa tetapi berfungsi untuk membuat kode lebih mudah dibaca. Ini tentu saja selalu merupakan masalah perdebatan, tetapi setidaknya ada beberapa konsep inti yang menurut sebagian besar programmer menyenangkan. Memberi nama variabel Anda dengan tepat, memberi nama secara umum, membuat baris Anda tidak terlalu panjang, menghindari fungsi panjang, enkapsulasi, hal-hal itu.
Namun, ada masalah yang saya belum menemukan orang mengomentari dan mungkin saja yang terbesar dari kelompok itu. Masalah argumen menjadi anonim saat Anda memanggil suatu fungsi.
Fungsi berasal dari matematika di mana f (x) memiliki makna yang jelas karena fungsi memiliki definisi yang jauh lebih ketat yang biasanya dilakukannya dalam pemrograman. Fungsi murni dalam matematika dapat melakukan jauh lebih sedikit daripada yang mereka bisa dalam pemrograman dan mereka adalah alat yang jauh lebih elegan, mereka biasanya hanya mengambil satu argumen (yang biasanya angka) dan mereka selalu mengembalikan satu nilai (juga biasanya angka). Jika suatu fungsi mengambil banyak argumen, mereka hampir selalu hanya merupakan dimensi ekstra dari domain fungsi. Dengan kata lain, satu argumen tidak lebih penting daripada yang lain. Mereka secara eksplisit dipesan, tentu, tetapi selain itu, mereka tidak memiliki pemesanan semantik.
Namun dalam pemrograman, kami memiliki lebih banyak fungsi mendefinisikan kebebasan, dan dalam hal ini saya berpendapat itu bukan hal yang baik. Situasi umum, Anda memiliki fungsi yang didefinisikan seperti ini
func DrawRectangleClipped (rectToDraw, fillColor, clippingRect) {}
Melihat definisi tersebut, jika fungsinya ditulis dengan benar, sangat jelas apa itu. Saat memanggil fungsi, Anda bahkan mungkin memiliki beberapa keajaiban penyelesaian intellisense / kode yang terjadi di IDE / editor Anda yang akan memberi tahu Anda apa argumen selanjutnya. Tapi tunggu. Jika saya membutuhkannya ketika saya benar-benar menulis panggilan, bukankah ada sesuatu yang kita lewatkan di sini? Orang yang membaca kode tidak memiliki manfaat dari IDE dan kecuali mereka melompat ke definisi, mereka tidak tahu yang mana dari dua persegi panjang yang dilewati karena argumen digunakan untuk apa.
Masalahnya bahkan lebih jauh dari itu. Jika argumen kami berasal dari beberapa variabel lokal, mungkin ada situasi di mana kita bahkan tidak tahu apa argumen kedua karena kita hanya melihat nama variabel. Ambil contoh baris kode ini
DrawRectangleClipped(deserializedArray[0], deserializedArray[1], deserializedArray[2])
Ini dikurangi ke berbagai luasan dalam bahasa yang berbeda tetapi bahkan dalam bahasa yang diketik dengan ketat dan bahkan jika Anda memberi nama variabel Anda dengan masuk akal, Anda bahkan tidak menyebutkan jenis variabelnya ketika Anda meneruskannya ke fungsi.
Seperti biasanya dengan pemrograman, ada banyak solusi potensial untuk masalah ini. Banyak sudah diterapkan dalam bahasa populer. Parameter yang dinamai dalam C # misalnya. Namun, semua yang saya tahu memiliki kelemahan signifikan. Memberi nama setiap parameter pada setiap panggilan fungsi tidak mungkin menghasilkan kode yang dapat dibaca. Hampir terasa seperti mungkin kita mengatasi kemungkinan yang diberikan oleh pemrograman teks biasa. Kami telah pindah dari teks HANYA di hampir setiap area, namun kami masih kode yang sama. Informasi lebih lanjut diperlukan untuk ditampilkan dalam kode? Tambahkan lebih banyak teks. Ngomong-ngomong, ini menjadi sedikit singgung jadi saya akan berhenti di sini.
Satu balasan yang saya dapatkan pada potongan kode kedua adalah bahwa Anda mungkin akan terlebih dahulu membongkar array ke beberapa variabel bernama dan kemudian menggunakannya tetapi nama variabel dapat berarti banyak hal dan cara namanya tidak selalu memberi tahu Anda cara seharusnya. ditafsirkan dalam konteks fungsi yang disebut. Dalam lingkup lokal, Anda mungkin memiliki dua persegi panjang bernama leftRectangle dan rightRectangle karena itulah yang mereka wakili secara semantik, tetapi tidak perlu diperluas ke apa yang mereka wakili ketika diberikan ke suatu fungsi.
Bahkan, jika variabel Anda dinamai dalam konteks fungsi yang dipanggil daripada Anda memperkenalkan informasi kurang dari Anda berpotensi dengan panggilan fungsi dan pada tingkat tertentu jika memang mengarah ke kode kode yang lebih buruk. Jika Anda memiliki prosedur yang menghasilkan persegi panjang yang Anda simpan di rectForClipping dan kemudian prosedur lain yang menyediakan rectForDrawing, maka panggilan sebenarnya ke DrawRectangleClipped hanyalah upacara. Baris yang berarti tidak ada yang baru dan ada agar komputer tahu apa yang sebenarnya Anda inginkan meskipun Anda sudah menjelaskannya dengan penamaan Anda. Ini bukan hal yang baik.
Saya benar-benar ingin mendengar perspektif baru tentang ini. Saya yakin saya bukan orang pertama yang menganggap ini masalah, jadi bagaimana cara mengatasinya?
Jawaban:
Saya setuju bahwa cara fungsi sering digunakan dapat menjadi bagian yang membingungkan dari penulisan kode, dan terutama membaca kode.
Jawaban untuk masalah ini sebagian tergantung pada bahasa. Seperti yang Anda sebutkan, C # memiliki parameter bernama. Solusi Objective-C untuk masalah ini melibatkan nama metode yang lebih deskriptif. Misalnya,
stringByReplacingOccurrencesOfString:withString:
adalah metode dengan parameter yang jelas.Di Groovy, beberapa fungsi mengambil peta, memungkinkan sintaksis seperti berikut:
Secara umum, Anda dapat mengatasi masalah ini dengan membatasi jumlah parameter yang Anda berikan ke suatu fungsi. Saya pikir 2-3 adalah batas yang baik. Jika tampaknya suatu fungsi membutuhkan lebih banyak parameter, itu menyebabkan saya untuk memikirkan kembali desain. Tetapi, ini bisa lebih sulit untuk dijawab secara umum. Terkadang Anda mencoba melakukan terlalu banyak dalam suatu fungsi. Terkadang masuk akal untuk mempertimbangkan kelas untuk menyimpan parameter Anda. Juga, dalam praktiknya, saya sering menemukan bahwa fungsi yang mengambil banyak parameter biasanya memiliki banyak dari mereka sebagai opsional.
Bahkan dalam bahasa seperti Objective-C, masuk akal untuk membatasi jumlah parameter. Salah satu alasannya adalah banyak parameter bersifat opsional. Sebagai contoh, lihat rangeOfString: dan variasinya di NSString .
Pola yang sering saya gunakan di Jawa adalah menggunakan kelas gaya-lancar sebagai parameter. Sebagai contoh:
Ini menggunakan kelas sebagai parameter, dan dengan kelas gaya fasih, membuat kode mudah dibaca.
Cuplikan Java di atas juga membantu pengaturan urutan parameter yang mungkin tidak terlalu jelas. Kami biasanya menganggap dengan koordinat bahwa X datang sebelum Y. Dan saya biasanya melihat tinggi sebelum lebar sebagai sebuah konvensi, tetapi itu masih tidak begitu jelas (
something.draw(5, 20)
).Saya juga melihat beberapa fungsi seperti
drawWithHeightAndWidth(5, 20)
tetapi bahkan ini tidak dapat mengambil terlalu banyak parameter, atau Anda akan mulai kehilangan keterbacaan.sumber
Dimension(int width, int height)
danGridLayout(int rows, int cols)
(jumlah baris adalah tinggi, artinyaGridLayout
memiliki tinggi terlebih dahulu danDimension
lebarnya).array_filter($input, $callback)
versusarray_map($callback, $input)
,strpos($haystack, $needle)
versusarray_search($needle, $haystack)
Sebagian besar diselesaikan dengan penamaan fungsi, parameter, dan argumen yang baik. Anda sudah menjelajahi itu dan menemukan kekurangannya. Sebagian besar kekurangan tersebut dimitigasi dengan menjaga fungsi tetap kecil, dengan sejumlah kecil parameter, baik dalam konteks panggilan dan konteks yang dipanggil. Contoh khusus Anda bermasalah karena fungsi yang Anda panggil sedang mencoba melakukan beberapa hal sekaligus: tentukan persegi panjang dasar, tentukan daerah kliping, gambar, dan isi dengan warna tertentu.
Ini seperti mencoba menulis kalimat hanya menggunakan kata sifat. Masukkan lebih banyak kata kerja (panggilan fungsi) di sana, buat subjek (objek) untuk kalimat Anda, dan lebih mudah dibaca:
Bahkan jika
clipRect
dancolor
memiliki nama yang buruk (dan mereka tidak seharusnya), Anda masih bisa membedakan tipe mereka dari konteksnya.Contoh deserialisasi Anda bermasalah karena konteks panggilan mencoba melakukan terlalu banyak sekaligus: deserialisasi dan menggambar sesuatu. Anda perlu menetapkan nama yang masuk akal dan memisahkan kedua tanggung jawab dengan jelas. Minimal, sesuatu seperti ini:
Banyak masalah keterbacaan disebabkan oleh upaya untuk menjadi terlalu ringkas, melompati tahap peralihan yang dibutuhkan manusia untuk memahami konteks dan semantik.
sumber
Dalam praktiknya, ini diselesaikan dengan desain yang lebih baik. Sangat luar biasa untuk fungsi yang ditulis dengan baik untuk mengambil lebih dari 2 input, dan ketika itu terjadi, itu tidak biasa bagi banyak input untuk tidak dapat diagregasi ke dalam beberapa ikatan kohesif. Ini membuatnya cukup mudah untuk memecah fungsi atau parameter agregat sehingga Anda tidak membuat fungsi melakukan terlalu banyak. Satu memiliki dua input, menjadi mudah untuk memberi nama dan lebih jelas tentang input mana yang.
Bahasa mainan saya memiliki konsep frasa untuk menangani hal ini, dan bahasa pemrograman lain yang lebih fokus pada bahasa alami memiliki pendekatan lain untuk menghadapinya, tetapi mereka semua cenderung memiliki kelemahan lain. Plus, bahkan frasa sedikit lebih dari sintaks yang bagus membuat fungsi memiliki nama yang lebih baik. Akan selalu sulit untuk membuat nama fungsi yang baik ketika dibutuhkan banyak input.
sumber
Dalam Javascript (atau ECMAScript ), misalnya, banyak programmer terbiasa
Dan sebagai praktik pemrograman, program ini beralih dari pemrogram ke perpustakaan mereka dan dari sana ke pemrogram lain yang tumbuh menyukainya dan menggunakannya serta menulis lebih banyak perpustakaan dll.
Contoh
Alih-alih menelepon
seperti ini:
, yang merupakan gaya yang valid dan benar, Anda memanggil
seperti ini:
, yang valid dan benar serta menyenangkan sehubungan dengan pertanyaan Anda.
Tentu saja, harus ada kondisi yang cocok untuk ini - dalam Javascript ini jauh lebih layak daripada di, katakanlah, C. Dalam javascript, ini bahkan melahirkan notasi struktural yang sekarang banyak digunakan yang tumbuh populer sebagai mitra yang lebih ringan untuk XML. Ini disebut JSON (Anda mungkin sudah pernah mendengarnya).
sumber
params
(sering berisi argumen opsional dan sering sendiri opsional) seperti misalnya, fungsi ini . Ini membuat fungsi dengan banyak argumen cukup mudah untuk dipahami (dalam contoh saya ada 2 argumen wajib dan 6 opsi).Anda harus menggunakan objektif-C, di sini adalah definisi fungsi:
Dan ini dia digunakan:
Saya pikir ruby memiliki konstruksi yang sama dan Anda dapat mensimulasikannya dalam bahasa lain dengan daftar nilai-kunci.
Untuk fungsi yang kompleks di Java, saya ingin mendefinisikan variabel dummy dalam fungsi kata-kata. Untuk contoh kiri-kanan Anda:
Sepertinya lebih banyak pengkodean, tetapi misalnya Anda dapat menggunakan leftRectangle dan kemudian memperbaiki kode nanti dengan "Ekstrak variabel lokal" jika Anda pikir itu tidak akan dimengerti oleh pengelola kode di masa mendatang, yang mungkin atau mungkin bukan Anda.
sumber
Pendekatan saya adalah membuat variabel lokal sementara - tetapi tidak hanya memanggil mereka
LeftRectange
danRightRectangle
. Sebaliknya, saya menggunakan nama yang agak panjang untuk menyampaikan lebih banyak makna. Saya sering mencoba untuk membedakan nama-nama sebanyak mungkin, misalnya tidak memanggil keduanyasomething_rectangle
, jika peran mereka tidak terlalu simetris.Contoh (C ++):
dan saya bahkan mungkin menulis fungsi atau template pembungkus satu-liner:
(abaikan ampersand jika Anda tidak tahu C ++).
Jika fungsi melakukan beberapa hal aneh tanpa deskripsi yang baik - maka mungkin perlu di refactored!
sumber