Bagaimana cara menghindari pengecualian yang menjengkelkan?

21

Membaca artikel Eric Lippert tentang pengecualian jelas merupakan pembuka mata tentang bagaimana saya harus mendekati pengecualian, baik sebagai produsen maupun sebagai konsumen. Namun, saya masih berjuang untuk mendefinisikan pedoman tentang bagaimana menghindari melemparkan pengecualian yang menjengkelkan.

Secara khusus:

  • Misalkan Anda memiliki metode Simpan yang dapat gagal karena a) Orang lain memodifikasi catatan sebelum Anda , atau b) Nilai yang Anda coba buat sudah ada . Kondisi ini diharapkan dan tidak luar biasa, jadi alih-alih melemparkan pengecualian Anda memutuskan untuk membuat versi Coba dari metode Anda, TrySave, yang mengembalikan boolean yang menunjukkan jika penyelamatan berhasil. Tetapi jika gagal, bagaimana konsumen tahu apa masalahnya? Atau akan lebih baik untuk mengembalikan enum yang menunjukkan hasil, seperti Ok / RecordAlreadyModified / ValueAlreadyExists? Dengan integer.TryParse masalah ini tidak ada, karena hanya ada satu alasan metode ini dapat gagal.
  • Apakah contoh sebelumnya benar-benar situasi yang menjengkelkan? Atau akankah melemparkan pengecualian dalam kasus ini menjadi cara yang disukai? Saya tahu itulah yang dilakukan di sebagian besar perpustakaan dan kerangka kerja, termasuk kerangka kerja Entity.
  • Bagaimana Anda memutuskan kapan akan membuat versi Coba dari metode Anda vs. menyediakan beberapa cara untuk menguji sebelumnya apakah metode itu akan berfungsi atau tidak? Saat ini saya mengikuti pedoman ini:
    • Jika ada kemungkinan kondisi balapan, maka buat versi Coba. Ini mencegah kebutuhan konsumen untuk menangkap pengecualian eksogen. Misalnya, dalam metode Simpan yang dijelaskan sebelumnya.
    • Jika metode untuk menguji kondisi cukup banyak akan melakukan semua yang metode asli lakukan, maka buat versi Coba. Misalnya, integer.TryParse ().
    • Dalam kasus lain, buat metode untuk menguji kondisi.
Mike
sumber
1
Contoh Anda tentang penyelamatan yang mungkin gagal sebenarnya bukan pengecualian yang sangat menjengkelkan. Ini cukup biasa dan mungkin seharusnya menjadi pengecualian.
S.Lott
@ S.Lott: Apa maksudmu dengan itu cukup biasa? Situasi itu sendiri, atau melemparkan pengecualian dalam situasi ini? Ngomong-ngomong, saya setuju dengan Anda bahwa tidak jelas apakah ini sebenarnya situasi yang menjengkelkan. Saya akan memperbarui pertanyaan.
Mike
"Situasi itu sendiri, atau melemparkan pengecualian dalam situasi ini" Keduanya.
S.Lott

Jawaban:

24

Misalkan Anda memiliki metode Simpan yang dapat gagal karena a) Orang lain memodifikasi catatan sebelum Anda, atau b) Nilai yang Anda coba buat sudah ada. Kondisi ini diharapkan dan tidak luar biasa, jadi alih-alih melemparkan pengecualian Anda memutuskan untuk membuat versi Coba dari metode Anda, TrySave, yang mengembalikan boolean yang menunjukkan jika penyelamatan berhasil. Tetapi jika gagal, bagaimana konsumen tahu apa masalahnya?

Pertanyaan bagus.

Pertanyaan pertama yang muncul di benak saya adalah: jika data sudah ada di sana dalam arti apa penyelamatan gagal ? Pasti kedengarannya berhasil bagi saya. Tetapi mari kita asumsikan demi argumen bahwa Anda benar-benar memiliki banyak alasan berbeda mengapa operasi bisa gagal.

Pertanyaan kedua yang muncul di benak saya adalah: apakah informasi yang ingin Anda kembalikan kepada pengguna dapat ditindaklanjuti ? Yaitu, apakah mereka akan mengambil keputusan berdasarkan informasi itu?

Ketika lampu "mesin periksa" menyala, saya membuka kap mobil, memverifikasi bahwa ada mesin di mobil saya yang tidak terbakar, dan membawanya ke garasi. Tentu saja di garasi mereka memiliki semua jenis peralatan diagnostik tujuan khusus yang memberi tahu mereka mengapa lampu engine menyala, tetapi dari sudut pandang saya , sistem peringatan dirancang dengan baik. Saya tidak peduli apakah masalahnya karena sensor oksigen merekam tingkat oksigen abnormal di ruang bakar, atau karena detektor kecepatan idle dicabut, atau apa pun. Saya akan mengambil tindakan yang sama, yaitu, biarkan orang lain memikirkan hal ini .

Apakah penelepon peduli mengapa penyelamatan gagal? Apakah mereka akan melakukan sesuatu tentang hal itu, selain menyerah atau mencoba lagi?

Mari kita asumsikan demi argumen bahwa penelepon benar-benar akan mengambil tindakan yang berbeda tergantung pada alasan mengapa operasi gagal.

Pertanyaan ketiga yang terlintas dalam pikiran adalah: apakah mode kegagalan luar biasa ? Saya pikir Anda mungkin bingung dengan tidak mungkin . Saya akan memikirkan dua pengguna yang berusaha mengubah catatan yang sama pada saat yang sama sebagai situasi yang luar biasa tetapi mungkin, bukan situasi yang umum.

Mari kita asumsikan demi argumen bahwa itu tidak biasa.

Pertanyaan keempat yang muncul di benak saya adalah: adakah cara untuk mendeteksi situasi buruk sebelumnya dengan andal?

Jika situasi buruk ada dalam ember "eksogen" saya, maka, tidak. Tidak ada cara untuk mengatakan "apakah pengguna lain mengubah catatan ini?" karena mereka mungkin memodifikasinya setelah Anda mengajukan pertanyaan . Jawabannya basi segera setelah dihasilkan.

Pertanyaan kelima yang terlintas dalam pikiran adalah: apakah ada cara untuk merancang API sehingga situasi buruk dapat dicegah?

Misalnya, Anda dapat membuat operasi "simpan" memerlukan dua langkah. Langkah satu: dapatkan kunci pada catatan yang sedang dimodifikasi. Operasi itu berhasil atau gagal sehingga dapat mengembalikan Boolean. Penelepon kemudian dapat memiliki kebijakan tentang bagaimana menghadapi kegagalan: tunggu sebentar dan coba lagi, menyerah, apa pun. Langkah dua: setelah kunci diperoleh, lakukan simpan dan lepaskan kunci. Sekarang save selalu berhasil sehingga tidak perlu khawatir tentang segala jenis penanganan kesalahan. Jika penyelamatan gagal, itu benar-benar luar biasa.

Eric Lippert
sumber
Semua poin yang sangat bagus, terima kasih. Sekarang inilah pertanyaan retoris yang mungkin merangkum posting saya: Jika Anda mendesain ulang File.Open (), apakah Anda akan membuat File.TryOpen ()? Bagaimana Anda mengomunikasikan alasan kegagalan kepada konsumen? Atau apakah melemparkan eksogen benar-benar kompromi terbaik di sini?
Mike
10
@ Mike: Sistem file adalah contoh yang baik tentang penggunaan pengecualian eksogen. Mereka jarang gagal, jadi kegagalan itu luar biasa. Mereka gagal secara tak terduga dan karena alasan yang sepenuhnya di luar kendali penelepon (tidak ada "kunci" yang dapat Anda ambil yang membuat kabel ethernet tetap terhubung), dan kegagalannya beragam dan dapat ditindaklanjuti (yaitu, kegagalan karena file adalah tidak ditemukan vs file ditemukan tetapi Anda tidak memiliki akses tulis keduanya dapat ditindaklanjuti dengan cara yang berbeda.) Semua ini adalah alasan untuk menyatakan kegagalan sebagai pengecualian.
Eric Lippert
Saya memang mempertimbangkan pertanyaan itu untuk dijawab sekarang, tetapi saya hanya ingin tahu;) Jika metode ini dapat gagal karena 2 atau lebih alasan, kegagalan itu dapat ditindaklanjuti, kegagalan itu tidak ada pengecualian dan kegagalan itu tidak dapat dideteksi sebelumnya atau dicegah, apa yang akan kamu lakukan?
Mike
@ Mike Saya tidak bisa berbicara untuk Eric, tapi itu kedengarannya tempat yang bagus untuk kode kesalahan. Mengembalikan anggota enum, mungkin.
Matius Baca
1

Dalam contoh Anda, jika situasi ValueAlreadyExists dapat dengan mudah diperiksa, itu harus diperiksa dan pengecualian dapat dimunculkan sebelum mencoba Simpan, saya tidak berpikir Coba harus diperlukan dalam situasi ini. Kondisi balapan lebih sulit untuk diperiksa sebelumnya, jadi membungkus Simpan dalam Coba dalam hal ini mungkin ide yang sangat bagus.

Secara umum, jika ada kondisi yang saya pikir sangat mungkin (seperti NoDataReturned, DivideByZero, dll ...) ATAU sangat mudah untuk memeriksa (seperti koleksi kosong atau nilai NULL), saya mencoba memeriksa sebelumnya dan menghadapinya sebelum saya sampai ke titik di mana saya harus menangkap pengecualian. Saya akui itu tidak selalu mudah untuk mengetahui kondisi ini sebelumnya, kadang-kadang mereka hanya muncul ketika kode sedang dalam pengujian ketat.

FrustratedWithFormsDesigner
sumber
0

The save()Metode harus melempar pengecualian.

Lapisan paling atas harus menangkap dan memberi tahu pengguna , tanpa menghentikan program, kecuali jika itu adalah program baris perintah Unix, dalam hal ini OK untuk mengakhiri.

Nilai pengembalian bukan cara yang baik untuk mengelola pengecualian.

Tulains Córdova
sumber