Untuk menangani beberapa kemungkinan kesalahan yang seharusnya tidak menghentikan eksekusi, saya memiliki error
variabel yang dapat diperiksa dan digunakan klien untuk melempar pengecualian. Apakah ini Anti-Pola? Apakah ada cara yang lebih baik untuk menangani ini? Untuk contoh tindakan ini, Anda dapat melihat API mysqli PHP . Asumsikan bahwa masalah visibilitas (pengakses, ruang lingkup publik dan pribadi, apakah variabel dalam kelas atau global?) Ditangani dengan benar.
44
try
/catch
ada. Selain itu, Anda dapat meletakkantry
/catch
lebih jauh ke atas tumpukan di lokasi yang lebih tepat untuk menanganinya (memungkinkan pemisahan masalah yang lebih besar).Jawaban:
Jika suatu bahasa secara inheren mendukung pengecualian, maka lebih disukai untuk melemparkan pengecualian dan klien dapat menangkap pengecualian jika mereka tidak ingin itu mengakibatkan kegagalan. Faktanya, klien kode Anda mengharapkan pengecualian dan akan mengalami banyak bug karena mereka tidak akan memeriksa nilai pengembalian.
Ada beberapa keuntungan menggunakan pengecualian jika Anda punya pilihan.
Pesan
Pengecualian berisi pesan kesalahan yang dapat dibaca pengguna yang dapat digunakan oleh pengembang untuk debugging atau bahkan ditampilkan kepada pengguna jika diinginkan. Jika kode pengkonsumsi tidak dapat menangani pengecualian, selalu dapat mencatatnya sehingga pengembang dapat menelusuri log tanpa harus berhenti di setiap penelusuran lainnya untuk mencari tahu apa nilai pengembalian dan memetakannya dalam tabel untuk mencari tahu apa yang menjadi pengecualian aktual.
Dengan nilai pengembalian, tidak ada informasi tambahan yang dapat diberikan dengan mudah. Beberapa bahasa akan mendukung pembuatan panggilan metode untuk mendapatkan pesan kesalahan terakhir, sehingga masalah ini sedikit berkurang, tetapi itu membutuhkan penelepon untuk membuat panggilan tambahan dan kadang-kadang akan memerlukan akses ke 'objek khusus' yang membawa informasi ini.
Dalam hal pesan pengecualian, saya memberikan konteks sebanyak mungkin, seperti:
Bandingkan ini dengan kode pengembalian -85. Manakah yang Anda pilih?
Panggil tumpukan
Pengecualian biasanya juga memiliki tumpukan panggilan terperinci yang membantu men-debug kode lebih cepat dan lebih cepat, dan juga dapat dicatat oleh kode panggilan jika diinginkan. Ini memungkinkan pengembang untuk menentukan masalah biasanya ke garis yang tepat, dan karenanya sangat kuat. Sekali lagi, bandingkan ini dengan file log dengan nilai balik (seperti -85, 101, 0, dll.), Yang mana yang Anda inginkan?
Pendekatan bias cepat gagal
Jika suatu metode dipanggil di suatu tempat yang gagal, itu akan mengeluarkan pengecualian. Kode panggilan harus menekan pengecualian secara eksplisit atau akan gagal. Saya telah menemukan ini benar-benar luar biasa karena selama pengembangan dan pengujian (dan bahkan dalam produksi) kode gagal dengan cepat, memaksa pengembang untuk memperbaikinya. Dalam hal nilai pengembalian, jika pemeriksaan untuk nilai balik terlewatkan, kesalahan diabaikan secara diam-diam dan bug muncul di tempat yang tidak terduga, biasanya dengan biaya yang jauh lebih tinggi untuk debug dan perbaikan.
Pengecualian Pembungkus dan Pembukaan
Pengecualian dapat dimasukkan ke dalam pengecualian lain dan kemudian dibuka jika diperlukan. Misalnya, kode Anda mungkin dilempar ke
ArgumentNullException
mana kode panggilan mungkin membungkus di dalamUnableToRetrievePolicyException
karena operasi itu gagal dalam kode panggilan. Sementara pengguna mungkin diperlihatkan pesan yang mirip dengan contoh yang saya berikan di atas, beberapa kode diagnostik mungkin membuka bukaan pengecualian dan menemukan bahwaArgumentNullException
telah menyebabkan masalah, yang berarti itu adalah kesalahan pengkodean dalam kode konsumen Anda. Ini kemudian dapat mengaktifkan peringatan sehingga pengembang dapat memperbaiki kode. Skenario lanjutan seperti itu tidak mudah diimplementasikan dengan nilai pengembalian.Kesederhanaan kode
Yang ini sedikit lebih sulit untuk dijelaskan, tetapi saya belajar melalui pengkodean ini baik dengan nilai pengembalian maupun pengecualian. Kode yang ditulis menggunakan nilai kembali biasanya akan melakukan panggilan dan kemudian memiliki serangkaian pemeriksaan pada apa nilai balik itu. Dalam beberapa kasus, itu akan membuat panggilan ke metode lain, dan sekarang akan memiliki serangkaian pemeriksaan untuk nilai-nilai kembali dari metode itu. Dengan pengecualian, penanganan pengecualian jauh lebih sederhana di sebagian besar atau tidak semua kasus. Anda memiliki blok coba / tangkap / akhirnya, dengan runtime mencoba yang terbaik untuk mengeksekusi kode di blok akhirnya untuk pembersihan. Bahkan blok try / catch / akhirnya yang bersarang relatif lebih mudah untuk ditindaklanjuti dan dipelihara dibandingkan dengan nested if / else dan nilai pengembalian terkait dari berbagai metode.
Kesimpulan
Jika platform yang Anda gunakan mendukung pengecualian (khususnya Java atau .NET), maka Anda harus mengasumsikan bahwa tidak ada cara lain selain melempar pengecualian karena platform ini memiliki panduan untuk melempar pengecualian, dan klien Anda akan mengharapkan begitu. Jika saya menggunakan perpustakaan Anda, saya tidak akan repot untuk memeriksa nilai kembali karena saya perkirakan pengecualian akan dilempar, seperti itulah dunia di platform ini.
Namun, jika itu adalah C ++, maka itu akan sedikit lebih sulit untuk ditentukan karena basis kode yang besar sudah ada dengan kode kembali, dan sejumlah besar pengembang disetel untuk mengembalikan nilai yang bertentangan dengan pengecualian (misalnya Windows penuh dengan HRESULT) . Selain itu, dalam banyak aplikasi, itu bisa menjadi masalah kinerja juga (atau setidaknya dianggap).
sumber
ErrorStateReturnVariable
kelas-super, dan salah satu propertinya adalahInnerErrorState
(yang merupakan turunan dariErrorStateReturnVariable
), yang mengimplementasikan sub-kelas dapat diatur untuk menunjukkan rantai kesalahan ... oh, tunggu. : pVariabel kesalahan adalah peninggalan dari bahasa seperti C, di mana pengecualian tidak tersedia. Hari ini, Anda harus menghindarinya kecuali ketika Anda sedang menulis perpustakaan yang berpotensi digunakan dari program C (atau bahasa serupa tanpa penanganan pengecualian).
Tentu saja, jika Anda memiliki jenis kesalahan yang bisa lebih baik diklasifikasikan sebagai "peringatan" (= perpustakaan Anda dapat memberikan hasil yang valid dan penelepon dapat mengabaikan peringatan jika menurutnya itu tidak penting), maka indikator status dalam formulir variabel dapat masuk akal bahkan dalam bahasa dengan pengecualian. Tapi waspadalah. Penelepon perpustakaan cenderung mengabaikan peringatan seperti itu bahkan jika mereka tidak seharusnya. Jadi pikirkan dua kali sebelum memperkenalkan konstruksi seperti itu ke lib Anda.
sumber
Ada beberapa cara untuk memberi sinyal kesalahan:
Masalah dari variabel kesalahan adalah mudah lupa untuk memeriksa.
Masalah pengecualian adalah yang menciptakan jalur eksekusi yang tersembunyi, dan, meskipun percobaan / tangkapan mudah untuk ditulis, memastikan pemulihan yang tepat dalam klausa tangkapan benar-benar sulit dilakukan (tidak ada dukungan dari sistem tipe / kompiler).
Masalah penangan kondisi adalah bahwa mereka tidak menyusun dengan baik: jika Anda memiliki eksekusi kode dinamis (fungsi virtual), maka tidak mungkin untuk memprediksi kondisi mana yang harus ditangani. Selain itu, jika kondisi yang sama dapat dinaikkan di beberapa titik, tidak ada yang mengatakan bahwa solusi yang seragam dapat diterapkan setiap kali, dan dengan cepat menjadi berantakan.
Pengembalian polimorfik (
Either a b
dalam Haskell) adalah solusi favorit saya sejauh ini:Satu-satunya masalah adalah bahwa mereka berpotensi menyebabkan pemeriksaan berlebihan; bahasa yang menggunakannya memiliki idiom untuk mengaitkan panggilan fungsi yang menggunakannya, tetapi mungkin masih membutuhkan pengetikan / kekacauan yang lebih sedikit. Di Haskell ini akan menjadi monad ; namun, ini jauh lebih menakutkan daripada kedengarannya, lihat Railway Oriented Programming .
sumber
Saya pikir itu mengerikan. Saat ini saya sedang melakukan refactoring aplikasi Java yang menggunakan nilai balik alih-alih pengecualian. Meskipun Anda mungkin sama sekali tidak bekerja dengan Java, saya pikir ini berlaku.
Anda berakhir dengan kode seperti ini:
Atau ini:
Saya lebih suka memiliki tindakan melemparkan pengecualian sendiri, sehingga Anda berakhir dengan sesuatu seperti:
Anda bisa membungkusnya dalam try-catch, dan mendapatkan pesan dari pengecualian, atau Anda dapat memilih untuk mengabaikan pengecualian, misalnya ketika Anda menghapus sesuatu yang mungkin sudah hilang. Ini juga menjaga jejak stack Anda, jika Anda memilikinya. Metode itu sendiri menjadi lebih mudah juga. Alih-alih menangani pengecualian sendiri, mereka hanya membuang apa yang salah.
Kode (mengerikan) saat ini:
Baru dan ditingkatkan:
Strack trace dipertahankan dan pesannya tersedia dalam pengecualian, alih-alih yang tidak berguna "Ada yang salah!".
Anda tentu saja dapat memberikan pesan kesalahan yang lebih baik, dan Anda harus melakukannya. Tetapi posting ini ada di sini karena kode yang saya kerjakan saat ini sangat menyebalkan, dan Anda tidak boleh melakukan hal yang sama.
sumber
throw new Exception("Something went wrong with " + instanceVar, ex);
"Untuk menangani beberapa kemungkinan kesalahan yang terjadi, itu seharusnya tidak menghentikan eksekusi,"
Jika Anda bermaksud bahwa kesalahan tidak boleh menghentikan pelaksanaan fungsi saat ini, tetapi harus dilaporkan ke pemanggil dengan cara tertentu - maka Anda memiliki beberapa opsi yang belum benar-benar disebutkan. Kasus ini benar-benar lebih merupakan peringatan daripada kesalahan. Melempar / Mengembalikan bukan opsi karena itu mengakhiri fungsi saat ini. Parameter pesan kesalahan tunggal atau kembali hanya memungkinkan paling banyak satu dari kesalahan ini terjadi.
Dua pola yang saya gunakan adalah:
Koleksi kesalahan / peringatan, baik diteruskan atau disimpan sebagai variabel anggota. Yang Anda tambahkan barang ke dan terus diproses. Saya pribadi tidak begitu menyukai pendekatan ini karena saya merasa itu melemahkan penelepon.
Mengirimkan objek penanganan kesalahan / peringatan (atau mengaturnya sebagai variabel anggota). Dan setiap kesalahan memanggil fungsi anggota dari pawang. Dengan cara ini penelepon dapat memutuskan apa yang harus dilakukan dengan kesalahan yang tidak berhenti tersebut.
Apa yang Anda sampaikan ke koleksi / penangan ini harus mengandung konteks yang cukup agar kesalahan ditangani "dengan benar" - Sebuah string biasanya terlalu sedikit, memberikannya beberapa contoh Pengecualian sering masuk akal - tetapi kadang-kadang disukai (sebagai penyalahgunaan Pengecualian) .
Kode umum yang menggunakan penangan kesalahan mungkin terlihat seperti ini
sumber
warnings
paket Python , yang memberikan pola lain untuk masalah ini.Seringkali tidak ada yang salah dengan menggunakan pola ini atau pola itu, selama Anda menggunakan pola yang digunakan orang lain. Dalam pengembangan Objective-C , pola yang lebih disukai adalah untuk melewatkan pointer di mana metode yang disebut dapat menyimpan objek NSError. Pengecualian disediakan untuk kesalahan pemrograman dan menyebabkan crash (kecuali jika Anda memiliki programmer Java atau .NET yang menulis aplikasi iPhone pertama mereka). Dan ini bekerja dengan sangat baik.
sumber
Pertanyaannya sudah dijawab, tetapi saya tidak bisa menahan diri.
Anda tidak bisa benar-benar mengharapkan Pengecualian untuk memberikan solusi untuk semua kasus penggunaan. Palu siapa pun?
Ada beberapa kasus di mana Pengecualian bukan merupakan akhir semua dan menjadi semua, misalnya, jika suatu metode menerima permintaan dan bertanggung jawab untuk memvalidasi semua bidang yang diteruskan, dan bukan hanya yang pertama, Anda harus berpikir bahwa itu mungkin untuk dilakukan. menunjukkan penyebab kesalahan selama lebih dari satu bidang. Harus dimungkinkan juga untuk menunjukkan apakah sifat validasi mencegah pengguna melangkah lebih jauh atau tidak. Contohnya adalah kata sandi yang tidak kuat. Anda dapat menampilkan pesan kepada pengguna yang menunjukkan bahwa kata sandi yang dimasukkan tidak terlalu kuat, tetapi cukup kuat.
Anda dapat berargumen bahwa semua validasi ini dapat dilemparkan sebagai pengecualian pada akhir modul validasi, tetapi mereka akan menjadi kode kesalahan dalam apa pun kecuali dalam nama.
Jadi pelajaran di sini adalah: Pengecualian ada di tempatnya, seperti halnya kode kesalahan. Pilih dengan bijak.
sumber
Validator
(antarmuka) yang disuntikkan ke dalam metode yang dimaksud (atau objek di belakangnya). Bergantung pada yang disuntikkanValidator
, metode akan melanjutkan dengan kata sandi yang buruk - atau tidak. Kode di sekitarnya kemudian dapat mencobaWeakValidator
jika pengguna memintanya setelah, misalnya,WeakPasswordException
dilemparkan oleh yang awalnya dicobaStrongValidator
.MiddlyStrongValidator
atau sesuatu. Dan jika itu tidak benar-benar mengganggu aliran Anda,Validator
harus sudah dipanggil sebelumnya, yaitu sebelum melanjutkan aliran saat pengguna masih memasukkan kata sandi mereka (atau serupa). Tetapi kemudian validasi itu bukan bagian dari metode yang dipertanyakan di tempat pertama. :) Mungkin masalah selera ...AggregateException
(atau yang serupaValidationException
), dan memberikan pengecualian khusus untuk setiap masalah validasi di InnerExceptions. Misalnya, bisa berupaBadPasswordException
: "Kata sandi pengguna kurang dari panjang minimum 6" atauMandatoryFieldMissingException
: "Nama depan harus disediakan untuk pengguna" dll. Ini tidak setara dengan kode kesalahan. Semua pesan ini dapat ditampilkan kepada pengguna dengan cara yang akan mereka pahami, dan jikaNullReferenceException
dilempar sebagai gantinya, maka kami mendapat bug.Ada kasus penggunaan adalah kode kesalahan lebih disukai daripada pengecualian.
Jika kode Anda dapat dilanjutkan meskipun ada kesalahan, tetapi perlu dilaporkan, maka pengecualian adalah pilihan yang buruk karena pengecualian menghentikan alur. Misalnya, jika Anda membaca dalam file data dan menemukan itu berisi beberapa data buruk non-terminal, mungkin lebih baik untuk membaca di sisa file dan melaporkan kesalahan daripada gagal sekaligus.
Jawaban lain telah mencakup mengapa pengecualian harus lebih disukai daripada kode kesalahan secara umum.
sumber
AcknowledgePossibleCorruption
metode. .Jelas tidak ada yang salah dengan tidak menggunakan pengecualian ketika pengecualian tidak cocok.
Ketika eksekusi kode tidak boleh terganggu (misalnya bertindak atas input pengguna yang mungkin mengandung banyak kesalahan, seperti program untuk dikompilasi atau formulir untuk diproses), saya menemukan bahwa mengumpulkan kesalahan dalam variabel kesalahan seperti
has_errors
danerror_messages
memang desain yang jauh lebih elegan daripada melempar pengecualian pada kesalahan pertama. Memungkinkan untuk menemukan semua kesalahan dalam input pengguna tanpa memaksa pengguna untuk mengirim ulang yang tidak perlu.sumber
Dalam beberapa bahasa pemrograman dinamis Anda dapat menggunakan nilai kesalahan dan penanganan pengecualian . Hal ini dilakukan dengan mengembalikan objek pengecualian yang tidak dilemparkan ke tempat nilai pengembalian biasa, yang dapat diperiksa seperti nilai kesalahan, tetapi itu melempar pengecualian jika tidak dicentang.
Dalam Perl 6 hal ini dilakukan melalui
fail
, yang jika dalamno fatal;
lingkup mengembalikanFailure
objek pengecualian khusus yang tidak dilewatkan .Dalam Perl 5 Anda dapat menggunakan Kontekstual :: Pengembalian Anda dapat melakukannya dengan
return FAIL
.sumber
Kecuali ada sesuatu yang sangat spesifik, saya pikir memiliki variabel kesalahan untuk validasi adalah ide yang buruk. Tujuannya tampaknya tentang menghemat waktu yang dihabiskan untuk validasi (Anda hanya dapat mengembalikan nilai variabel)
Tetapi jika Anda mengubah sesuatu, Anda harus menghitung ulang nilai itu. Saya tidak bisa mengatakan lebih banyak tentang berhenti dan melempar Exception.
EDIT: Saya tidak menyadari ini adalah masalah paradigma perangkat lunak, bukan kasus tertentu.
Izinkan saya mengklarifikasi poin-poin saya lebih lanjut dalam kasus khusus saya di mana jawaban saya masuk akal
Ada dua jenis kesalahan:
Di lapisan layanan, tidak ada pilihan selain menggunakan objek Hasil sebagai pembungkus yang merupakan kesetaraan variabel kesalahan. Mensimulasikan pengecualian melalui panggilan layanan pada protokol seperti http dimungkinkan, tetapi jelas bukan hal yang baik untuk dilakukan. Saya tidak berbicara tentang jenis kesalahan ini dan tidak berpikir ini adalah jenis kesalahan yang ditanyakan dalam pertanyaan ini.
Saya sedang berpikir tentang jenis kesalahan kedua. Dan jawaban saya adalah tentang jenis kesalahan kedua ini. Dalam objek entitas, ada pilihan untuk kita, beberapa di antaranya
Menggunakan variabel validasi sama dengan memiliki metode validasi tunggal untuk setiap objek entitas. Secara khusus, pengguna dapat mengatur nilai dengan cara yang menjaga setter sebagai setter murni, tidak ada efek samping (ini sering merupakan praktik yang baik) atau seseorang dapat memasukkan validasi ke dalam setiap setter dan kemudian menyimpan hasilnya ke dalam variabel validasi. Keuntungan dari ini adalah untuk menghemat waktu, hasil validasi di-cache ke dalam variabel validasi sehingga ketika pengguna memanggil validasi () beberapa kali, tidak perlu melakukan beberapa validasi.
Hal terbaik untuk dilakukan dalam hal ini adalah menggunakan metode validasi tunggal tanpa menggunakan validasi apa pun untuk kesalahan validasi cache. Ini membantu menjaga setter sebagai setter saja.
sumber