Kapan harus melempar pengecualian?

435

Saya memiliki pengecualian yang dibuat untuk setiap kondisi yang tidak diharapkan oleh aplikasi saya. UserNameNotValidException, PasswordNotCorrectExceptiondll.

Namun saya diberitahu bahwa saya tidak boleh membuat pengecualian untuk kondisi tersebut. Dalam UML saya, itu adalah pengecualian untuk aliran utama, jadi mengapa itu tidak menjadi pengecualian?

Adakah panduan atau praktik terbaik untuk membuat pengecualian?

Kwan Cheng
sumber
59
Harap buka kembali, ini adalah pertanyaan yang sangat masuk akal dan valid. Setiap pertanyaan melibatkan sejumlah pendapat, tetapi dalam kasus ini saya curiga ini adalah masalah 'praktik terbaik'.
Tim Long
19
+1 untuk dibuka kembali. Karena banyak topik menarik lainnya 'itu tergantung' dan sangat berguna untuk menganalisis pertukaran ketika membuat keputusan. Fakta bahwa orang mengacaukan pendapat dengan fakta dalam jawaban tidak meniadakan hal ini. Memilah-milah lumpur adalah latihan yang harus diserahkan kepada pembaca.
aron
12
Saya juga setuju pertanyaan ini harus dibuka kembali karena terkait dengan praktik terbaik. Ngomong-ngomong, praktik terbaik selalu merupakan opini yang dapat membantu orang lain.
Ajay Sharma
6
Microsoft mengatakan: "Jangan mengembalikan kode kesalahan. Pengecualian adalah cara utama melaporkan kesalahan dalam kerangka kerja." dan "... Jika seorang anggota tidak berhasil melakukan apa yang dirancang untuk dilakukan, itu harus dianggap sebagai kegagalan eksekusi dan pengecualian harus dilemparkan.". msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75
4
Ini mungkin pengecualian yang benar-benar masuk akal, itu hanya tergantung pada metode mana yang melemparnya. Metode yang dipanggil IsCredentialsValid(username,password)tidak boleh melemparkan pengecualian jika nama pengguna atau kata sandi tidak valid, tetapi kembalikan salah. Tetapi katakanlah suatu metode yang membaca data dari database dapat secara sah membuang pengecualian seperti itu, jika otentikasi gagal. Singkatnya: Anda harus melempar pengecualian jika suatu metode tidak dapat melakukan tugas yang seharusnya dilakukan.
JacquesB

Jawaban:

629

Pedoman pribadi saya adalah: pengecualian dilemparkan ketika asumsi mendasar dari blok kode saat ini ternyata salah.

Contoh 1: katakan saya memiliki fungsi yang seharusnya memeriksa kelas arbitrer dan mengembalikan true jika kelas itu mewarisi dari Daftar <>. Fungsi ini mengajukan pertanyaan, "Apakah objek ini adalah keturunan Daftar?" Fungsi ini seharusnya tidak pernah membuang pengecualian, karena tidak ada area abu-abu dalam operasinya - setiap kelas tunggal baik yang diwariskan atau tidak dari Daftar <>, jadi jawabannya selalu "ya" atau "tidak".

Contoh 2: katakan saya memiliki fungsi lain yang memeriksa Daftar <> dan mengembalikan true jika panjangnya lebih dari 50, dan salah jika panjangnya kurang. Fungsi ini menanyakan pertanyaan, "Apakah daftar ini memiliki lebih dari 50 item?" Tetapi pertanyaan ini membuat asumsi - mengasumsikan bahwa objek yang diberikan adalah daftar. Jika saya berikan NULL, maka anggapan itu salah. Dalam hal ini, jika kembali fungsi baik benar atau salah, maka itu adalah melanggar aturan sendiri. Fungsi tidak dapat mengembalikan apa pun dan mengklaim bahwa itu menjawab pertanyaan dengan benar. Jadi itu tidak kembali - itu melempar pengecualian.

Ini sebanding dengan kekeliruan logis "pertanyaan yang dimuat" . Setiap fungsi mengajukan pertanyaan. Jika input yang diberikan membuat pertanyaan itu salah, maka lontarkan pengecualian. Baris ini lebih sulit untuk digambarkan dengan fungsi yang mengembalikan batal, tetapi intinya adalah: jika asumsi fungsi tentang inputnya dilanggar, ia harus membuang pengecualian daripada kembali normal.

Sisi lain dari persamaan ini adalah: jika Anda menemukan fungsi Anda sering melempar pengecualian, maka Anda mungkin perlu memperbaiki asumsi mereka.

The Digital Gabeg
sumber
15
Persis! Pengecualian dilemparkan ketika dan hanya ketika prasyarat fungsi (asumsi tentang argumen) rusak !
Lightman
11
Dalam linguistik ini kadang-kadang disebut kegagalan prasangka . Contoh klasik adalah untuk Bertrand Russell: "Apakah Raja Perancis botak" tidak dapat dijawab ya atau tidak, (resp. "Raja Prancis botak" tidak benar atau salah), karena berisi anggapan yang salah , yaitu bahwa ada Raja Prancis. Kegagalan prasangka sering terlihat dengan deskripsi yang pasti, dan itu biasa terjadi saat pemrograman. Misalnya "Kepala daftar" memiliki kegagalan anggapan ketika daftar kosong, dan kemudian pantas untuk melemparkan pengecualian.
Mohan
Ini mungkin penjelasan terbaik!
gaurav
Terima kasih. Begitu. Banyak. "Apakah Raja Perancis botak". Saya pernah mendengar ini sebelumnya ketika meneliti hutan meinong .... :) Terima kasih. @Mohan
ErlichBachman
285

Karena mereka adalah hal-hal yang akan terjadi secara normal. Pengecualian bukanlah mekanisme kontrol aliran. Pengguna sering mendapatkan kata sandi yang salah, ini bukan kasus yang luar biasa. Pengecualian harus menjadi hal yang benar-benar langka, UserHasDiedAtKeyboardketik situasi.

blowdart
sumber
3
Hmm, tidak. Pengecualian dapat digunakan sebagai mekanisme kontrol aliran jika kinerja maksimum tidak diperlukan, yang berlaku untuk sebagian besar aplikasi web. Python menggunakan pengecualian 'StopIteration' untuk mengakhiri iterators, dan itu bekerja dengan sangat baik. Biayanya tidak seberapa dibandingkan dengan IO, dll.
Seun Osewa
9
+1 jawaban bagus. Saya sangat frustrasi oleh pengembang yang bekerja pada API yang harus saya konsumsi, dan membuang Pengecualian untuk setiap hal kecil. SANGAT beberapa kasus benar-benar memerlukan pengecualian. Jika Anda memiliki 25 jenis pengecualian yang ditentukan, lihat kembali desain Anda, Anda mungkin salah melakukannya.
7wp
1
haruskah ada pengecualian ketika pengguna mencoba tindakan ilegal yang tidak diizinkan dengan memanipulasi kode halaman web, untuk misalnya menghapus posting orang lain di StackOverflow?
Rajat Gupta
30
Pengecualian ADALAH mekanisme kontrol aliran. Anda bisa melemparnya. Anda bisa menangkapnya. Anda memindahkan kontrol ke bagian kode lain. Itu aliran kontrol. Satu-satunya alasan bahasa memiliki pengecualian sama sekali adalah agar Anda dapat menulis kode langsung tanpa bertanya "apakah hal itu gagal?" setelah semua yang kamu lakukan. Haskell, misalnya, tidak memiliki pengecualian karena monads dan notasi dapat mengotomatiskan kesalahan saat memeriksa Anda.
Jesse
2
Pengecualian lebih dari mekanisme kontrol aliran. Mereka memberikan (metode) informasi yang bermanfaat kepada klien tentang hasil luar biasa yang harus mereka sadari dan tangani. Artinya, digunakan dengan benar, pengecualian membuat API lebih kuat
idelvall
67

Pedoman kecil saya sangat dipengaruhi oleh buku "Kode lengkap":

  • Gunakan pengecualian untuk memberi tahu tentang hal-hal yang tidak boleh diabaikan.
  • Jangan gunakan pengecualian jika kesalahan dapat ditangani secara lokal
  • Pastikan pengecualian berada pada tingkat abstraksi yang sama dengan sisa rutinitas Anda.
  • Pengecualian harus disediakan untuk apa yang benar - benar luar biasa .
Commander Keen
sumber
35

Ini BUKAN pengecualian jika nama pengguna tidak valid atau kata sandi tidak benar. Itulah hal-hal yang harus Anda harapkan dalam aliran operasi normal. Pengecualian adalah hal-hal yang bukan bagian dari operasi program normal dan agak jarang.

EDIT: Saya tidak suka menggunakan pengecualian karena Anda tidak bisa tahu apakah metode melempar pengecualian hanya dengan melihat panggilan. Karena itulah pengecualian hanya boleh digunakan jika Anda tidak dapat menangani situasi dengan cara yang layak (pikirkan "kehabisan memori" atau "komputer menyala").

EricSchaefer
sumber
"Saya tidak suka menggunakan pengecualian karena Anda tidak bisa tahu apakah suatu metode melempar pengecualian hanya dengan melihat panggilan." Inilah sebabnya mengapa ada pengecualian yang diperiksa untuk bahasa yang mendukungnya.
Newtopian
1
Pengecualian yang dicentang memiliki masalah sendiri. Saya masih lebih suka menggunakan pengecualian "keadaan luar biasa", bukan untuk hal-hal yang merupakan bagian dari alur kerja normal.
EricSchaefer
2
Menanggapi hasil edit Anda. Saya selalu memasukkan dokumen xml saya di akhir bagian ringkasan pengecualian bahwa fungsi melempar sehingga saya bisa melihat informasi itu di intellisense.
Matthew Vines
1
haruskah ada pengecualian ketika pengguna mencoba tindakan ilegal yang tidak diizinkan dengan memanipulasi kode halaman web, untuk misalnya menghapus posting orang lain di StackOverflow?
Rajat Gupta
1
Sepertinya Anda berbicara tentang menangani kesalahan (memori kami, komputer menyala) vs menangani pengecualian kode (catatan hilang, tipe input tidak valid, dll). Saya pikir ada perbedaan yang jelas antara keduanya.
Chuck Burgess
28

Satu aturan praktis adalah dengan menggunakan pengecualian dalam hal sesuatu yang biasanya tidak dapat Anda prediksi. Contohnya adalah konektivitas database, file yang hilang pada disk, dll. Untuk skenario yang dapat Anda prediksi, yaitu pengguna yang mencoba masuk dengan kata sandi yang buruk Anda harus menggunakan fungsi yang mengembalikan boolean dan tahu bagaimana menangani situasi dengan anggun. Anda tidak ingin mengakhiri eksekusi dengan tiba-tiba dengan melemparkan pengecualian hanya karena seseorang salah ketik kata sandi mereka.

japollock
sumber
6
Anda tidak perlu menghentikan eksekusi program dengan pengecualian ... melempar pengecualian, penelepon kemudian menangkap pengecualian dan harus menanganinya, jika mungkin, masuk dan kesalahan dan pindah. Ini adalah 'bentuk buruk' untuk terus melempar pengecualian ke tumpukan panggilan - tangkap di mana itu terjadi, dan tangani di sana
Krakkos
2
tetapi mengapa bahkan membuangnya jika Anda bisa mengatasinya secara langsung. jika kata sandi salah atau ada sesuatu yang salah, saya biarkan saja mengembalikan kesalahan dan memberikan kesalahan
My1
" file tidak ada pada disk " Sebagian besar kerangka kerja bahasa, misalnya .NET framework, menyediakan API untuk memeriksa keberadaan file juga. Mengapa tidak menggunakannya sebelum mengakses file secara langsung!
user1451111
23

Yang lain mengusulkan bahwa pengecualian tidak boleh digunakan karena login yang buruk diharapkan dalam aliran normal jika pengguna salah ketik. Saya tidak setuju dan saya tidak mendapatkan alasan. Bandingkan dengan membuka file .. jika file tersebut tidak ada atau tidak tersedia karena suatu alasan maka pengecualian akan dilemparkan oleh framework. Menggunakan logika di atas ini adalah kesalahan oleh Microsoft. Mereka seharusnya mengembalikan kode kesalahan. Sama untuk penguraian, permintaan web, dll., Dll.

Saya tidak menganggap bagian login yang buruk dari aliran normal, ini luar biasa. Biasanya pengguna mengetik kata sandi yang benar, dan file itu ada. Kasing luar biasa luar biasa dan tidak apa-apa menggunakan pengecualian untuk itu. Menyulitkan kode Anda dengan menyebarkan nilai kembali melalui level n di atas tumpukan adalah pemborosan energi dan akan menghasilkan kode berantakan. Lakukan hal paling sederhana yang mungkin bisa berhasil. Jangan mengoptimalkan secara prematur dengan menggunakan kode kesalahan, hal-hal luar biasa menurut definisi jarang terjadi, dan pengecualian tidak dikenakan biaya apa pun kecuali Anda membuangnya.

Bjorn Reppen
sumber
Kecuali Anda dapat memeriksa bahwa file tersebut ada sebelum memanggil terbuka (tergantung pada kerangka kerja Anda tentu saja) Jadi fasilitas itu ada dan dengan demikian jika file hilang antara cek dan Anda mencoba membukanya maka itu pengecualian.
blowdart
7
File yang ada tidak berarti pengguna diperbolehkan menulis ke file misalnya. Memeriksa setiap masalah yang mungkin terjadi sangat membosankan dan rawan kesalahan. + Anda menduplikasi kode (KERING).
Bjorn Reppen
Satu hal dengan Pengecualian kata sandi yang tidak valid adalah bahwa setiap kelambatan dibandingkan dengan solusi kode kembali tidak akan terlihat bagi pengguna manusia mengetikkan kata sandi.
kuda kertas
7
"Menyulitkan kode Anda dengan menyebarkan nilai kembali melalui n tingkat atas tumpukan adalah pemborosan energi dan akan menghasilkan kode berantakan". Bagi saya, itu adalah alasan yang sangat kuat untuk menggunakan pengecualian. Kode yang baik biasanya terdiri dari fungsi-fungsi kecil. Anda tidak ingin melewatkan kode kesalahan itu berulang-ulang dari satu fungsi kecil ke lainnya.
beluchin
Saya pikir kebingungan hasil dari asumsi mungkin bahwa hasil yang dapat diprediksi dari loginmetode-jenis adalah bahwa kata sandi mungkin salah, mungkin sebenarnya digunakan untuk menentukan ini, dan tidak ingin memiliki pengecualian dalam kasus ini; sedangkan dalam file openskenario jenis, yang memiliki hasil tertentu yang diinginkan - jika sistem tidak dapat memberikan hasil karena parameter input yang salah atau beberapa faktor eksternal, itu adalah penggunaan pengecualian yang logis.
theMayer
17

Pengecualian adalah efek yang agak mahal, jika misalnya Anda memiliki pengguna yang memberikan kata sandi tidak valid, biasanya merupakan ide yang lebih baik untuk mengirimkan kembali tanda kegagalan, atau beberapa indikator lain yang tidak valid.

Hal ini disebabkan oleh cara penanganan pengecualian, input buruk yang benar, dan item berhenti penting yang unik harus menjadi pengecualian, tetapi bukan info login yang gagal.

Penjual Mitchel
sumber
14

Saya pikir Anda hanya harus melempar pengecualian ketika tidak ada yang dapat Anda lakukan untuk keluar dari kondisi Anda saat ini. Misalnya jika Anda mengalokasikan memori dan tidak ada yang mengalokasikan. Dalam kasus yang Anda sebutkan, Anda dapat dengan jelas pulih dari status tersebut dan dapat mengembalikan kode kesalahan kembali ke pemanggil Anda.


Anda akan melihat banyak saran, termasuk dalam jawaban untuk pertanyaan ini, bahwa Anda harus memberikan pengecualian hanya dalam keadaan "luar biasa". Kelihatannya masuk akal, tetapi saran yang salah, karena itu menggantikan satu pertanyaan ("kapan saya harus melemparkan pengecualian") dengan pertanyaan subjektif lain ("apa yang luar biasa"). Sebagai gantinya, ikuti saran Herb Sutter (untuk C ++, tersedia di artikel Dr Dobbs When and How to Use Exception , dan juga dalam bukunya bersama Andrei Alexandrescu, Standar Pengodean C ++ ): berikan pengecualian jika, dan hanya jika

  • sebuah prasyarat tidak terpenuhi (yang biasanya membuat salah satu dari yang berikut tidak mungkin) atau
  • alternatif akan gagal memenuhi kondisi pasca atau
  • alternatif akan gagal mempertahankan invarian.

Kenapa ini lebih baik? Bukankah itu menggantikan pertanyaan dengan beberapa pertanyaan tentang prasyarat, kondisi pascakondisi dan invarian? Ini lebih baik karena beberapa alasan yang terhubung.

  • Prasyarat, postkondisi dan invarian adalah karakteristik desain program kami (API internal), sedangkan keputusan untukthrow adalah detail implementasi. Ini memaksa kita untuk mengingat bahwa kita harus mempertimbangkan desain dan implementasinya secara terpisah, dan tugas kita saat menerapkan metode adalah menghasilkan sesuatu yang memenuhi batasan desain.
  • Ini memaksa kita untuk berpikir dalam hal prasyarat, postkondisi, dan invarian, yang merupakan satu - satunya asumsi yang harus dibuat oleh penelepon metode kita, dan diungkapkan secara tepat, sehingga memungkinkan penggabungan longgar antara komponen-komponen program kami.
  • Kopling longgar itu kemudian memungkinkan kita untuk memperbaiki implementasi, jika perlu.
  • Post-condition dan invarian dapat diuji; itu menghasilkan kode yang dapat dengan mudah diuji unit, karena post-kondisi adalah predikat kode unit-test kami dapat memeriksa (menegaskan).
  • Berpikir dalam hal kondisi pasca secara alami menghasilkan desain yang berhasil sebagai kondisi pasca , yang merupakan gaya alami untuk menggunakan pengecualian. Jalur eksekusi normal ("bahagia") dari program Anda ditata secara linier, dengan semua kode penanganan kesalahan dipindahkan ke catchklausa.
Raedwald
sumber
10

Saya akan mengatakan tidak ada aturan yang keras dan cepat tentang kapan harus menggunakan pengecualian. Namun ada alasan bagus untuk menggunakan atau tidak menggunakannya:

Alasan untuk menggunakan pengecualian:

  • Alur kode untuk kasus umum lebih jelas
  • Dapat mengembalikan informasi kesalahan kompleks sebagai objek (meskipun ini juga dapat dicapai menggunakan parameter "keluar" kesalahan yang dilewatkan oleh referensi)
  • Bahasa umumnya menyediakan beberapa fasilitas untuk mengelola pembersihan rapi jika terjadi pengecualian (coba / akhirnya di Jawa, gunakan dalam C #, RAII dalam C ++)
  • Jika tidak ada pengecualian yang dilemparkan, eksekusi kadang - kadang bisa lebih cepat daripada memeriksa kode kembali
  • Di Jawa, pengecualian yang dicek harus dinyatakan atau ditangkap (meskipun ini bisa menjadi alasan untuk menentang)

Alasan untuk tidak menggunakan pengecualian:

  • Kadang-kadang berlebihan jika penanganan kesalahan sederhana
  • Jika pengecualian tidak didokumentasikan atau dideklarasikan, mereka mungkin tidak tertangkap oleh kode panggilan, yang mungkin lebih buruk daripada jika kode panggilan hanya mengabaikan kode kembali (keluar aplikasi vs kegagalan diam - yang lebih buruk mungkin tergantung pada skenario)
  • Dalam C ++, kode yang menggunakan pengecualian harus pengecualian aman (bahkan jika Anda tidak melempar atau menangkapnya, tetapi memanggil fungsi melempar secara tidak langsung)
  • Dalam C ++, sulit untuk mengetahui kapan suatu fungsi mungkin melempar, oleh karena itu Anda harus paranoid tentang keamanan pengecualian jika Anda menggunakannya
  • Pengecualian melempar dan menangkap pada umumnya jauh lebih mahal dibandingkan dengan memeriksa bendera pengembalian

Secara umum, saya akan lebih cenderung menggunakan pengecualian di Java daripada di C ++ atau C #, karena saya berpendapat bahwa pengecualian, dinyatakan atau tidak, pada dasarnya merupakan bagian dari antarmuka formal fungsi, karena mengubah jaminan pengecualian Anda mungkin mematahkan kode panggilan. Keuntungan terbesar menggunakannya di Java IMO, adalah Anda tahu bahwa penelepon Anda HARUS menangani pengecualian, dan ini meningkatkan kemungkinan perilaku yang benar.

Karena itu, dalam bahasa apa pun, saya akan selalu mendapatkan semua pengecualian dalam lapisan kode atau API dari kelas umum, sehingga kode panggilan selalu dapat menjamin untuk menangkap semua pengecualian. Juga saya akan menganggap buruk untuk membuang kelas pengecualian yang khusus implementasi, ketika menulis API atau pustaka (yaitu bungkus pengecualian dari lapisan bawah sehingga pengecualian yang diterima pemanggil Anda dapat dipahami dalam konteks antarmuka Anda).

Perhatikan bahwa Java membuat perbedaan antara pengecualian umum dan Runtime di mana yang terakhir tidak perlu dinyatakan. Saya hanya akan menggunakan kelas pengecualian Runtime ketika Anda tahu bahwa kesalahan adalah hasil dari bug dalam program.

Robert
sumber
5

Kelas pengecualian seperti kelas "normal". Anda membuat kelas baru ketika "adalah" jenis objek yang berbeda, dengan bidang yang berbeda dan operasi yang berbeda.

Sebagai aturan praktis, Anda harus mencoba menyeimbangkan antara jumlah pengecualian dan rincian pengecualian. Jika metode Anda melempar lebih dari 4-5 pengecualian yang berbeda, Anda mungkin dapat menggabungkan beberapa dari mereka menjadi pengecualian yang lebih "umum", (misalnya dalam kasus Anda "AuthenticationFailedException"), dan menggunakan pesan pengecualian untuk merinci apa yang salah. Kecuali jika kode Anda menangani masing-masing secara berbeda, Anda tidak perlu membuat banyak kelas pengecualian. Dan jika itu terjadi, mungkin Anda harus mengembalikan enum dengan kesalahan yang terjadi. Ini sedikit lebih bersih dengan cara ini.

Shachar
sumber
5

Jika kode itu berjalan di dalam loop yang kemungkinan akan menyebabkan pengecualian berulang-ulang, maka melempar pengecualian bukanlah hal yang baik, karena mereka cukup lambat untuk N. besar. Tapi tidak ada yang salah dengan melempar pengecualian kustom jika kinerjanya tidak sebuah isu. Pastikan saja Anda memiliki pengecualian basis yang semuanya mewarisi, disebut BaseException atau sesuatu seperti itu. BaseException mewarisi System.Exception, tetapi semua pengecualian Anda mewarisi BaseException. Anda bahkan dapat memiliki jenis pohon Pengecualian untuk mengelompokkan jenis yang serupa, tetapi ini mungkin atau mungkin tidak berlebihan.

Jadi, jawaban singkatnya adalah jika itu tidak menyebabkan penalti kinerja yang signifikan (yang seharusnya tidak kecuali Anda melemparkan banyak pengecualian), maka silakan.

Charles Graham
sumber
Saya sangat menyukai komentar Anda tentang pengecualian di dalam satu lingkaran dan berpikir untuk mencobanya sendiri. Saya menulis sebuah contoh program yang menjalankan waktu loop int.MaxValuedan menghasilkan pengecualian 'bagi dengan nol' di dalamnya. Versi IF / ELSE, di mana saya memeriksa apakah dividen tidak nol sebelum operasi divisi , selesai dalam 6082 ms dan 15407722 ticks sedangkan versi TRY / CATCH, di mana saya menghasilkan pengecualian dan menangkap pengecualian , selesai di 28174385 ms dan 71371326155 ticks: kekalahan 4632 kali lebih banyak dari versi if / else.
user1451111
3

Saya setuju dengan japollock di atas sana - melempar pengakuan ketika Anda tidak yakin tentang hasil operasi. Panggilan ke API, mengakses sistem file, panggilan basis data, dll. Setiap kali Anda melewati "batas" bahasa pemrograman Anda.

Saya ingin menambahkan, jangan ragu untuk melempar pengecualian standar. Kecuali jika Anda akan melakukan sesuatu yang "berbeda" (abaikan, email, log, tunjukkan bahwa gambar paus twitter itu penting, dll.), Maka jangan repot-repot dengan pengecualian khusus.

dclaysmith
sumber
3

aturan praktis untuk melempar pengecualian cukup sederhana. Anda melakukannya ketika kode Anda telah memasuki kondisi INVALID yang tidak dapat dibatalkan. jika data dikompromikan atau Anda tidak dapat memutar kembali pemrosesan yang terjadi hingga titik tersebut maka Anda harus menghentikannya. memang apa lagi yang bisa kamu lakukan? logika pemrosesan Anda akhirnya akan gagal di tempat lain. jika Anda dapat memulihkan entah bagaimana kemudian lakukan itu dan jangan membuang pengecualian.

dalam kasus khusus Anda jika Anda dipaksa untuk melakukan sesuatu yang konyol seperti menerima penarikan uang dan hanya kemudian memeriksa pengguna / pasword Anda harus mengakhiri proses dengan melemparkan pengecualian untuk memberitahukan bahwa sesuatu yang buruk telah terjadi dan mencegah kerusakan lebih lanjut.

goran
sumber
2

Secara umum Anda ingin melemparkan pengecualian untuk apa pun yang dapat terjadi di aplikasi Anda yang "Luar Biasa"

Dalam contoh Anda, kedua pengecualian tersebut terlihat seperti Anda memanggilnya melalui validasi kata sandi / nama pengguna. Dalam hal ini dapat dikatakan bahwa tidak benar-benar luar biasa bahwa seseorang akan salah ketik nama pengguna / kata sandi.

Mereka adalah "pengecualian" untuk aliran utama UML Anda tetapi lebih banyak "cabang" dalam pemrosesan.

Jika Anda mencoba mengakses file passwd atau database Anda dan tidak bisa, itu akan menjadi kasus yang luar biasa dan akan menjamin membuang pengecualian.

Gord
sumber
" Jika Anda mencoba mengakses file passwd atau database Anda dan tidak bisa, itu akan menjadi kasus yang luar biasa dan akan menjamin membuang pengecualian. " Sebagian besar kerangka kerja bahasa, misalnya .NET framework, menyediakan API untuk memeriksa keberadaan file juga. Mengapa tidak menggunakannya sebelum mengakses file secara langsung!
user1451111
2

Pertama, jika pengguna API Anda tidak tertarik dengan kegagalan spesifik dan berbutir halus, maka pengecualian khusus untuk mereka tidak ada nilainya.

Karena seringkali tidak mungkin untuk mengetahui apa yang mungkin berguna bagi pengguna Anda, pendekatan yang lebih baik adalah dengan memiliki pengecualian khusus, tetapi pastikan mereka mewarisi dari kelas umum (misalnya, std :: exception atau turunannya dalam C ++). Itu memungkinkan klien Anda untuk menangkap pengecualian tertentu jika mereka memilih, atau pengecualian yang lebih umum jika mereka tidak peduli.

Jason Etheridge
sumber
2

Pengecualian dimaksudkan untuk acara yang merupakan perilaku abnormal, kesalahan, kegagalan, dan semacamnya. Perilaku fungsional, kesalahan pengguna, dll., Harus ditangani oleh logika program. Karena akun atau kata sandi yang buruk adalah bagian yang diharapkan dari aliran logika dalam rutin masuk, ia harus dapat menangani situasi tersebut tanpa kecuali.

Joe Skora
sumber
2

Saya memiliki masalah filosofis dengan penggunaan pengecualian. Pada dasarnya, Anda mengharapkan skenario tertentu terjadi, tetapi alih-alih menanganinya secara eksplisit, Anda mendorong masalah untuk ditangani "di tempat lain". Dan di mana "tempat lain" itu bisa ditebak siapa pun.

Dan
sumber
2

Saya akan mengatakan bahwa umumnya setiap fundamentalisme mengarah ke neraka.

Anda tentu tidak ingin berakhir dengan aliran yang didorong oleh pengecualian, tetapi menghindari pengecualian juga merupakan ide yang buruk. Anda harus menemukan keseimbangan antara kedua pendekatan. Yang tidak akan saya lakukan adalah membuat tipe pengecualian untuk setiap situasi luar biasa. Itu tidak produktif.

Apa yang umumnya saya sukai adalah membuat dua tipe dasar pengecualian yang digunakan di seluruh sistem: LogicalException dan TechnicalException . Ini lebih jauh dapat dibedakan oleh subtipe jika diperlukan, tetapi pada umumnya tidak diperlukan.

Pengecualian teknis menunjukkan pengecualian yang benar-benar tidak terduga seperti server database sedang down, koneksi ke layanan web melempar IOException dan sebagainya.

Di sisi lain pengecualian logis digunakan untuk menyebarkan situasi salah yang kurang parah ke lapisan atas (umumnya beberapa hasil validasi).

Harap dicatat bahwa bahkan pengecualian logis tidak dimaksudkan untuk digunakan secara teratur untuk mengontrol aliran program, tetapi lebih untuk menyoroti situasi ketika aliran harus benar-benar berakhir. Saat digunakan di Jawa, kedua jenis pengecualian adalah RuntimeException subclass dan penanganan kesalahan sangat berorientasi pada aspek.

Jadi, dalam contoh login, mungkin bijaksana untuk membuat sesuatu seperti AuthenticationException dan membedakan situasi konkret dengan nilai enum seperti UsernameNotExisting , PasswordMismatch dll. Maka Anda tidak akan berakhir dengan memiliki hierarki pengecualian yang sangat besar dan dapat menjaga blok tangkapan pada level yang dapat dipertahankan . Anda juga dapat dengan mudah menggunakan beberapa mekanisme penanganan pengecualian umum karena Anda memiliki pengecualian yang dikategorikan dan tahu dengan baik apa yang harus disebarkan ke pengguna dan bagaimana caranya.

Penggunaan khas kami adalah untuk membuang LogicalException selama panggilan Layanan Web ketika input pengguna tidak valid. Pengecualian di-marshall ke detail SOAPFault dan kemudian tidak di-marshall ke pengecualian lagi pada klien yang mengakibatkan menunjukkan kesalahan validasi pada satu bidang input halaman web tertentu karena pengecualian memiliki pemetaan yang tepat untuk bidang tersebut.

Ini tentu saja bukan satu-satunya situasi: Anda tidak perlu menekan layanan web untuk memunculkan pengecualian. Anda bebas untuk melakukannya dalam situasi luar biasa (seperti dalam kasus Anda harus gagal-cepat) - itu semua atas kebijakan Anda.

Petr Macek
sumber
2

bagi saya Pengecualian harus dilemparkan ketika aturan teknis atau bisnis yang disyaratkan gagal. misalnya jika entitas mobil dikaitkan dengan susunan 4 ban ... jika satu ban atau lebih adalah nol ... pengecualian harus Dipecat "NotEnoughTiresException", karena dapat ditangkap pada tingkat sistem yang berbeda dan memiliki signifikan artinya melalui logging. selain itu jika kita hanya mencoba mengendalikan aliran null dan mencegah instanciation mobil. kita mungkin tidak akan pernah menemukan sumber masalahnya, karena ban tidak seharusnya nol pada awalnya.

Genjuro
sumber
1

alasan utama untuk menghindari melempar pengecualian adalah bahwa ada banyak overhead yang terlibat dengan melemparkan pengecualian.

Satu hal yang dinyatakan dalam artikel di bawah ini adalah pengecualian untuk kondisi dan kesalahan luar biasa.

Nama pengguna yang salah tidak selalu merupakan kesalahan program tetapi kesalahan pengguna ...

Berikut ini adalah titik awal yang layak untuk pengecualian dalam .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

Sam
sumber
1

Pengecualian yang dilemparkan menyebabkan tumpukan melonggarkan, yang memiliki beberapa dampak kinerja (diakui, lingkungan yang dikelola modern telah meningkatkan hal itu). Masih berulang kali melempar dan menangkap pengecualian dalam situasi bersarang akan menjadi ide yang buruk.

Mungkin lebih penting dari itu, pengecualian dimaksudkan untuk kondisi luar biasa. Mereka tidak boleh digunakan untuk aliran kontrol biasa, karena ini akan merusak pembacaan kode Anda.

Arno
sumber
1

Saya memiliki tiga jenis kondisi yang saya tangkap.

  1. Masukan yang salah atau hilang tidak boleh menjadi pengecualian. Gunakan js sisi klien dan sisi server untuk mendeteksi, mengatur atribut, dan meneruskan kembali ke halaman yang sama dengan pesan.

  2. AppException. Ini biasanya merupakan pengecualian yang Anda deteksi dan lempar dengan kode Anda. Dengan kata lain ini adalah yang Anda harapkan (file tidak ada). Log itu, atur pesannya, dan teruskan kembali ke halaman kesalahan umum. Halaman ini biasanya memiliki sedikit info tentang apa yang terjadi.

  3. Pengecualian yang tak terduga. Ini adalah yang tidak Anda ketahui. Catat dengan detail dan teruskan ke halaman kesalahan umum.

Semoga ini membantu

Michael
sumber
1

Keamanan digabungkan dengan contoh Anda: Anda seharusnya tidak memberi tahu penyerang bahwa nama pengguna ada, tetapi kata sandi salah. Itu informasi tambahan yang tidak perlu Anda bagikan. Katakan saja "nama pengguna atau kata sandi salah."

segera
sumber
1

Jawaban sederhananya adalah, setiap kali operasi tidak mungkin (karena salah satu aplikasi ATAU karena akan melanggar logika bisnis). Jika suatu metode dipanggil dan tidak mungkin untuk melakukan apa metode itu ditulis untuk dilakukan, buanglah Pengecualian. Contoh yang baik adalah konstruktor selalu membuang ArgumentExceptions jika instance tidak dapat dibuat menggunakan parameter yang disediakan. Contoh lain adalah InvalidOperationException, yang dilemparkan ketika operasi tidak dapat dilakukan karena keadaan anggota lain atau anggota kelas.

Dalam kasus Anda, jika metode seperti Login (nama pengguna, kata sandi) dipanggil, jika nama pengguna tidak valid, memang benar untuk melempar UserNameNotValidException, atau PasswordNotCorrectException jika kata sandi salah. Pengguna tidak dapat masuk menggunakan parameter yang disediakan (yaitu tidak mungkin karena akan melanggar otentikasi), jadi lemparkan Pengecualian. Meskipun saya mungkin memiliki dua Pengecualian Anda mewarisi dari ArgumentException.

Karena itu, jika Anda ingin TIDAK melemparkan Pengecualian karena kegagalan login mungkin sangat umum, salah satu strategi adalah membuat metode yang mengembalikan tipe yang mewakili kegagalan yang berbeda. Ini sebuah contoh:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

Sebagian besar pengembang diajarkan untuk menghindari Pengecualian karena overhead yang disebabkan oleh melempar mereka. Sangat bagus untuk sadar sumber daya, tetapi biasanya tidak mengorbankan desain aplikasi Anda. Mungkin itulah alasan Anda diminta untuk tidak membuang dua Pengecualian Anda. Apakah akan menggunakan Pengecualian atau tidak biasanya bermuara pada seberapa sering Pengecualian akan terjadi. Jika ini adalah hasil yang cukup umum atau cukup diharapkan, inilah saat sebagian besar pengembang akan menghindari Pengecualian dan sebagai gantinya membuat metode lain untuk menunjukkan kegagalan, karena dugaan konsumsi sumber daya.

Berikut adalah contoh menghindari menggunakan Pengecualian dalam skenario seperti yang baru saja dijelaskan, menggunakan pola Coba ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}
Chris
sumber
1

Menurut saya, pertanyaan mendasarnya adalah apakah seseorang akan mengharapkan penelepon ingin melanjutkan aliran program normal jika suatu kondisi terjadi. Jika Anda tidak tahu, ada metode doSomething dan trySomething yang terpisah, di mana yang pertama mengembalikan kesalahan dan yang terakhir tidak, atau memiliki rutin yang menerima parameter untuk menunjukkan apakah pengecualian harus dilemparkan jika gagal). Pertimbangkan kelas untuk mengirim perintah ke sistem jarak jauh dan melaporkan respons. Perintah-perintah tertentu (misalnya restart) akan menyebabkan sistem jauh untuk mengirim respons tetapi kemudian menjadi non-responsif untuk jangka waktu tertentu. Karena itu berguna untuk dapat mengirim perintah "ping" dan mencari tahu apakah sistem jarak jauh merespons dalam jangka waktu yang wajar tanpa harus membuang pengecualian jika tidak t (penelepon mungkin akan berharap bahwa beberapa "ping" upaya pertama akan gagal, tetapi orang akhirnya akan bekerja). Di sisi lain, jika seseorang memiliki urutan perintah seperti:

  exchange_command ("open tempfile");
  exchange_command ("tulis data tempfile {apa pun}");
  exchange_command ("tulis data tempfile {apa pun}");
  exchange_command ("tulis data tempfile {apa pun}");
  exchange_command ("tulis data tempfile {apa pun}");
  exchange_command ("close tempfile");
  exchange_command ("salin tempfile ke realfile");

orang ingin kegagalan operasi apa pun untuk membatalkan seluruh urutan. Sementara satu dapat memeriksa setiap operasi untuk memastikan itu berhasil, itu lebih bermanfaat untuk memiliki exchange_command () rutin melempar pengecualian jika perintah gagal.

Sebenarnya, dalam skenario di atas, mungkin bermanfaat untuk memiliki parameter untuk memilih sejumlah mode penanganan kegagalan: jangan pernah melempar pengecualian, melempar pengecualian hanya untuk kesalahan komunikasi, atau melempar pengecualian dalam kasus di mana perintah tidak mengembalikan "kesuksesan" "indikasi.

supercat
sumber
1

"PasswordNotCorrectException" bukan contoh yang baik untuk menggunakan pengecualian. Pengguna yang salah mengartikan kata sandinya diharapkan, jadi ini bukan pengecualian IMHO. Anda bahkan mungkin pulih darinya, menampilkan pesan kesalahan yang bagus, jadi itu hanya pemeriksaan validitas.

Pengecualian yang tidak ditangani pada akhirnya akan menghentikan eksekusi - yang bagus. Jika Anda mengembalikan kode false, null, atau error, Anda harus berurusan dengan status program sendiri. Jika Anda lupa memeriksa kondisi di suatu tempat, program Anda mungkin tetap berjalan dengan data yang salah, dan Anda mungkin kesulitan mencari tahu apa yang terjadi dan di mana .

Tentu saja, Anda dapat menyebabkan masalah yang sama dengan pernyataan tangkapan kosong, tetapi setidaknya mengetahui itu lebih mudah dan tidak mengharuskan Anda untuk memahami logika.

Jadi sebagai aturan praktis:

Gunakan mereka di mana pun Anda tidak mau atau tidak bisa pulih dari kesalahan.

DanMan
sumber
0

Anda dapat menggunakan sedikit pengecualian umum untuk kondisi itu. Misalnya ArgumentException dimaksudkan untuk digunakan ketika ada yang salah dengan parameter ke metode (dengan pengecualian ArgumentNullException). Secara umum Anda tidak perlu pengecualian seperti LessThanZeroException, NotPrimeNumberException dll. Pikirkan pengguna metode Anda. Jumlah kondisi yang ingin dia tangani secara spesifik sama dengan jumlah jenis pengecualian yang harus dilempar metode Anda. Dengan cara ini, Anda dapat menentukan seberapa rinci pengecualian yang akan Anda miliki.

Ngomong-ngomong, selalu berusaha menyediakan beberapa cara bagi pengguna perpustakaan Anda untuk menghindari pengecualian. TryParse adalah contoh yang baik, ia ada sehingga Anda tidak harus menggunakan int.Parse dan menangkap pengecualian. Dalam kasus Anda, Anda mungkin ingin memberikan beberapa metode untuk memeriksa apakah nama pengguna valid atau kata sandi benar sehingga pengguna Anda (atau Anda) tidak perlu melakukan banyak penanganan pengecualian. Semoga ini menghasilkan kode yang lebih mudah dibaca dan kinerja yang lebih baik.

Serhat Ozgel
sumber
0

Pada akhirnya keputusan turun ke apakah lebih bermanfaat untuk menangani kesalahan tingkat aplikasi seperti ini menggunakan penanganan pengecualian, atau melalui mekanisme linting Anda sendiri seperti mengembalikan kode status. Saya tidak berpikir ada aturan yang sulit dan cepat tentang mana yang lebih baik, tetapi saya akan mempertimbangkan:

  • Siapa yang memanggil kode Anda? Apakah ini semacam API publik atau perpustakaan internal?
  • Bahasa apa yang Anda gunakan? Jika itu Java, misalnya, maka melempar pengecualian (dicentang) akan membebani penelepon Anda untuk menangani kondisi kesalahan ini dengan cara tertentu, sebagai lawan dari status pengembalian yang dapat diabaikan. Itu bisa baik atau buruk.
  • Bagaimana kondisi kesalahan lain dalam aplikasi yang sama ditangani? Penelepon tidak akan mau berurusan dengan modul yang menangani kesalahan dengan cara istimewa tidak seperti yang lain dalam sistem.
  • Berapa banyak hal yang bisa salah dengan rutinitas yang bersangkutan, dan bagaimana mereka akan ditangani secara berbeda? Pertimbangkan perbedaan antara serangkaian blok tangkap yang menangani kesalahan yang berbeda dan beralih pada kode kesalahan.
  • Apakah Anda memiliki informasi terstruktur tentang kesalahan yang harus Anda kembalikan? Melempar pengecualian memberi Anda tempat yang lebih baik untuk menyimpan informasi ini daripada hanya mengembalikan status.
eli
sumber
0

Ada dua kelas utama pengecualian:

1) Pengecualian sistem (mis. Koneksi basis data terputus) atau 2) Pengecualian pengguna. (mis. Validasi input pengguna, 'kata sandi salah')

Saya merasa terbantu untuk membuat Kelas Pengecualian Pengguna saya sendiri dan ketika saya ingin melempar kesalahan pengguna, saya ingin ditangani secara berbeda (yaitu kesalahan sumber daya ditampilkan kepada pengguna) maka semua yang perlu saya lakukan dalam penangan kesalahan utama saya adalah memeriksa jenis objek :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If
Keras
sumber
0

Beberapa hal berguna untuk dipikirkan ketika memutuskan apakah pengecualian sesuai:

  1. tingkat kode apa yang ingin Anda jalankan setelah kandidat pengecualian terjadi - yaitu, berapa banyak lapisan tumpukan panggilan yang harus dilepas. Anda biasanya ingin menangani pengecualian sedekat mungkin ke tempat itu terjadi. Untuk validasi nama pengguna / kata sandi, Anda biasanya akan menangani kegagalan dalam blok kode yang sama, daripada membiarkan gelembung pengecualian muncul. Jadi pengecualian mungkin tidak tepat. (OTOH, setelah tiga upaya login gagal, aliran kontrol dapat bergeser ke tempat lain, dan pengecualian mungkin sesuai di sini.)

  2. Apakah acara ini sesuatu yang ingin Anda lihat di log kesalahan? Tidak setiap pengecualian ditulis ke log kesalahan, tetapi berguna untuk bertanya apakah entri ini dalam log kesalahan akan berguna - yaitu, Anda akan mencoba melakukan sesuatu tentang hal itu, atau akan menjadi sampah yang Anda abaikan.

Mike Kantor
sumber