Di mana kesalahannya, Billy? Kami membutuhkan kesalahan ...


Beberapa waktu lalu, kolega saya menerbitkan artikel tentang penanganan kesalahan di Jawa / Kotlin. Dan menjadi menarik bagi saya metode transmisi kesalahan apa yang ada dalam pemrograman pada umumnya. Jika Anda juga tertarik, maka yang di bawahnya adalah hasil riset. Kemungkinan besar, beberapa metode eksotis telah dihilangkan, tetapi di sini hanya ada satu harapan untuk komentar, yang kadang-kadang lebih menarik dan berguna pada artikel Habré. :)

Dalam sejarah bahasa pemrograman, tidak banyak cara telah ditemukan untuk menyampaikan kesalahan. Jika kita benar-benar melepaskan diri, hanya ada tiga dari mereka: pengembalian langsung dari suatu fungsi, transfer kontrol, dan pengaturan negara. Segala sesuatu yang lain, pada tingkat tertentu, merupakan campuran dari pendekatan-pendekatan ini. Di bawah ini saya mencoba mengumpulkan dan menggambarkan perwakilan utama dari ketiga spesies ini.

Penafian: untuk singkat dan penyederhanaan persepsi untuk setiap kode dieksekusi terisolasi yang menghasilkan kesalahan, saya akan menggunakan kata "fungsi", dan untuk entitas non-primitif (integer, string, boolean, dll ...) - "struktur".

Pengembalian langsung


Pengembalian langsung itu sederhana. Meskipun ini mungkin metode yang paling umum digunakan, tetapi ada banyak pilihan. Metode pemrosesan menyatukan mereka semua - membandingkan nilai kembali dengan nilai yang telah ditentukan.

  1. Kembalikan status run. Opsi paling dangkal adalah BENAR (jika dijalankan secara normal) atau SALAH (jika ada kegagalan).
  2. Kembalikan nilai yang benar jika sukses dan salah jika terjadi kesalahan.
    C / c ++
    Fungsi strchr () mengembalikan pointer ke kemunculan pertama karakter ch dalam string yang ditunjukkan oleh str. Jika ch tidak ditemukan, NULL dikembalikan.

    Cukup sering pendekatan 1 dan 2 digunakan bersamaan dengan pengaturan negara.
  3. Kembalikan kode kesalahan. Jika kita ingin tidak hanya tahu bahwa eksekusi berakhir dengan salah, tetapi juga untuk memahami di mana kesalahan terjadi pada fungsi. Biasanya, jika fungsi selesai tanpa kesalahan, kode 0 dikembalikan. Dalam kasus kesalahan, kode digunakan untuk menentukan tempat tertentu di tubuh fungsi di mana terjadi kesalahan. Tapi ini bukan aturan besi, lihat, misalnya, di HTTP dengan 200.
  4. Kembalikan kode kesalahan dalam rentang nilai yang tidak valid. Misalnya, biasanya suatu fungsi harus mengembalikan bilangan bulat positif, dan jika terjadi kesalahan, kodenya dengan tanda minus.

    function countElements(param) { if (!isArray(param)) { return -10; } else if(!isInitialized(param)){ return -20 } else { return count(array); } } 

  5. Kembalikan berbagai jenis untuk hasil positif dan negatif. Misalnya, secara nominal - string, tetapi tidak secara nominal - nomor atau kelas Sukses dan Kesalahan kelas.

     sealed class UserProfileResult { data class Success(val userProfile: UserProfileDTO) : UserProfileResult() data class Error(val message: String, val cause: Exception? = null) : UserProfileResult() } val avatarUrl = when (val result = client.requestUserProfile(userId)) { is UserProfileResult.Success -> result.userProfile.avatarUrl is UserProfileResult.Error -> "http://domain.com/defaultAvatar.png" } 

    Anda juga dapat memanggil kembali baik dari dunia pemrograman fungsional. Meski di sini Anda bisa berdebat.
  6. Mengembalikan struktur yang berisi hasil itu sendiri dan kesalahan.

     function doSomething(): array { ... if($somethingWrong === true) { return ["result" => null, "error" => "Alarm!!!"]; } else { return ["result" => $result, "error" => null]; } ... } 

  7. Kembalikan beberapa nilai. Pada awalnya saya cenderung untuk tidak memisahkan metode ini dari yang sebelumnya, tetapi pada akhirnya saya memutuskan untuk memasukkannya ke dalam paragraf terpisah. Opsi ini cukup langka, karena dapat digunakan secara eksklusif dalam bahasa yang memungkinkan Anda mengembalikan beberapa nilai dari suatu fungsi, dan tidak banyak dari mereka. Contoh yang paling mencolok, tetapi bukan satu-satunya adalah bahasa Go .

     f, err := Sqrt(-1) if err != nil { fmt.Println(err) } 

Pengaturan negara


Versi tertua dan hardcore, yang tidak kehilangan relevansinya hingga hari ini. Ini terdiri dalam kenyataan bahwa fungsi tidak mengembalikan apa pun, dan dalam kasus kesalahan ia menulis nilainya (dalam bentuk apa pun) ke entitas yang terpisah, apakah itu register prosesor, variabel global atau bidang kelas privat. Untuk menangani kesalahan semacam ini, Anda perlu mengekstraksi nilai secara independen dari tempat yang tepat dan memeriksanya.

  1. Mengatur negara "global". Saya mengutipnya karena paling sering kita berbicara tentang globalitas dalam lingkup tertentu.

     # ls /unknown/path 2>/dev/null # echo $? 1 

  2. Pengaturan negara Anda sendiri. Ketika kami memiliki beberapa struktur menyediakan fungsi. Fungsi menetapkan keadaan untuk struktur ini, dan kesalahan sudah diekstraksi dari struktur baik secara langsung atau menggunakan fungsi khusus lainnya.

     $mysqli = new mysqli("localhost", "my_user", "my_password", "world"); $result = $mysqli->query("SET a=1"); if ($mysqli->errno) { printf(" : %d\n", $mysqli->errno); } 

  3. Mengatur keadaan objek yang dikembalikan. Sangat menggema dengan paragraf 6. dari bagian sebelumnya. Tidak seperti paragraf sebelumnya, pemeriksaan negara dilakukan pada struktur yang dikembalikan, dan bukan pada yang menyediakan fungsi. Sebagai contoh nyata, protokol HTTP dan pustaka yang tak terhitung jumlahnya dalam berbagai bahasa bekerja dengannya.

     Response response = client.newCall("https://www.google.com").execute(); Integer errorCode = response.getCode(); 


Kontrol transfer


Dan sekarang kita sampai pada paradigma yang paling modis. Pengecualian, panggilan balik, penangan kesalahan global - semua ini. Apa yang menyatukan mereka semua adalah bahwa dalam hal terjadi kesalahan, kontrol ditransfer ke penangan yang telah ditentukan, dan bukan ke kode yang disebut fungsi.

  1. Pengecualian Semua orang tahu melempar / mencoba / menangkap. Melempar pengecualian, fungsi membentuk struktur yang menjelaskan kesalahan dan, paling sering, berisi berbagai metadata yang berguna yang memfasilitasi diagnosis masalah (misalnya, panggilan stack). Setelah itu, struktur ini diteruskan ke mekanisme khusus yang "memutar kembali" di sepanjang tumpukan panggilan ke blok percobaan pertama, yang dikaitkan dengan tangkapan, yang dapat menangani pengecualian dari jenis ini. Metode ini bagus karena seluruh logika melempar pengecualian diimplementasikan oleh runtime itu sendiri. Hal yang sama buruk, karena biaya overhead (mari kita tanpa holivarov :)).
  2. Penangan kesalahan global. Bukan cara yang paling umum, tetapi memang demikian. Saya bahkan tidak tahu harus berkata apa di sini. Mungkin perlu disebutkan bahwa di sini juga dapat dikaitkan mekanisme browser: ketika kode bekerja jauh dari arus utama memantau peristiwa yang tiba di dalamnya.

     function myErrorHandler($errno, $errstr, $errfile, $errline) { echo "<b>Custom error:</b> [$errno] $errstr<br>"; echo " Error on line $errline in $errfile<br>"; } set_error_handler("myErrorHandler"); 

  3. Telepon balik. Mereka sangat dicintai oleh pengembang untuk Android, JavaScript dan pembela untuk pemrograman reaktif. Intinya sederhana: selain data yang diproses, fungsi-fungsi handler ditransfer ke fungsi. Jika terjadi kesalahan, fungsi utama akan memanggil penangan yang sesuai dan meneruskannya.

     var observer = Rx.Observer.create( x => console.log(`onNext: ${x}`), e => console.log(`onError: ${e}`), () => console.log('onCompleted')); 


Tampaknya tidak melupakan apa pun.

Dan fakta yang lucu. Mungkin cara paling orisinal untuk mengembalikan kesalahan, menggabungkan pada saat yang bersamaan pengecualian, mengatur negara dan mengembalikan beberapa nilai, saya bertemu di Informix SPL (Saya menulis dari memori):

 CREATE PROCEDURE some_proc(...) RETURNING int, int, int, int; … ON EXCEPTION SET SQLERR, ISAMERR RETURN 0, SQLERR, ISAMERR, USRERR; END EXCEPTION; LET USRERR = 1; -- do Something That May Raise Exception LET USRERR = 2; -- do Something Other That May Raise Exception … RETURN result, 0, 0, 0; END PROCEDURE 

Source: https://habr.com/ru/post/id472506/


All Articles