Penanganan kesalahan - Jika suatu program gagal karena kesalahan atau mengabaikannya secara diam-diam

20

Saya sedang menulis sebuah program kecil sederhana untuk mengirimkan MIDI melalui jaringan. Saya tahu bahwa program akan mengalami masalah transmisi dan / atau situasi pengecualian lain yang tidak dapat saya prediksi.

Untuk penanganan pengecualian, saya melihat dua pendekatan. Haruskah saya menulis program sehingga:

  • gagal dengan bang ketika ada masalah atau
  • haruskah hanya mengabaikan kesalahan dan melanjutkan, dengan mengorbankan integritas data?

Pendekatan mana yang diharapkan oleh pengguna secara wajar?
Apakah ada cara yang lebih baik untuk menangani pengecualian?

Selain itu, haruskah keputusan saya tentang penanganan pengecualian dipengaruhi oleh apakah saya berurusan dengan koneksi jaringan atau tidak (mis. Sesuatu yang bisa saya perkirakan memiliki masalah)?

Arlen Beiler
sumber
1
@ GlenH7 Membayar ke depan ... :)
Agak
Apakah ada cara untuk memberi +1 pada suntingan? ;) Oh, tunggu, saya mengikuti Anda. Tentu, akan dilakukan :)
Arlen Beiler
2
"Pendekatan mana yang diharapkan pengguna?" Apakah Anda mencoba bertanya kepada salah satu pengguna Anda? Pengujian kegunaan lorong bisa sangat efektif. Lihat blog.openhallway.com/?p=146
Bryan Oakley
Saya tidak punya pengguna :)
Arlen Beiler

Jawaban:

34

Jangan pernah Anda mengabaikan kesalahan yang ditemui program Anda. Minimal, Anda harus login ke file atau mekanisme lain untuk pemberitahuan. Ada mungkin ada situasi sesekali mana Anda akan ingin mengabaikan kesalahan tapi dokumen itu! Jangan menulis catchblok kosong tanpa komentar yang menjelaskan mengapa kosong.

Apakah program harus gagal atau tidak tergantung banyak pada konteksnya. Jika Anda dapat menangani kesalahan dengan anggun, lakukanlah. Jika itu adalah kesalahan yang tidak terduga, maka program Anda akan macet. Itu cukup mendasar dari penanganan pengecualian.

marco-fiset
sumber
18
Tidak cukup untuk menjamin -1, tetapi "tidak pernah" adalah kata yang kuat, dan ada situasi di mana mengabaikan kesalahan adalah tindakan yang tepat. Misalnya, perangkat lunak untuk memecahkan kode transmisi siaran HDTV. Ketika Anda menemukan beberapa kesalahan, yang sering Anda lakukan, yang dapat Anda lakukan adalah mengabaikannya dan terus menguraikan apa yang terjadi sesudahnya.
whatsisname
1
@whatsisname: true, tetapi Anda harus dengan jelas mendokumentasikan mengapa Anda mengabaikannya. Jawaban yang diperbarui untuk mempertimbangkan komentar Anda.
marco-fiset
1
@ marco-fiset saya tidak setuju. Menghancurkan seluruh program hanyalah solusi jika ini adalah situasi di mana memulai kembali akan memperbaiki masalah. Ini tidak selalu terjadi, jadi menabrak sering kali sia-sia dan default bahwa perilaku itu benar-benar bodoh. Mengapa Anda ingin menunda seluruh operasi Anda karena kesalahan lokal? Itu tidak masuk akal. Sebagian besar aplikasi melakukannya dan, untuk beberapa alasan, programmer benar-benar baik-baik saja dengan itu.
MaiaVictor
@Dokkat: intinya adalah untuk tidak melanjutkan dengan nilai yang salah, yang akan memberikan solusi yang salah (sampah masuk, sampah keluar). Crashing memang memperbaiki masalah: ini memaksa pengguna untuk memberikan input yang berbeda atau mengganggu pengembang memperbaiki program mereka;)
André Paramés
1
@whatsisname Saya pikir Anda mungkin ingin memikirkan definisi "kesalahan" Anda dalam kasus itu. Kesalahan dalam data yang diterima tidak sama dengan kesalahan dalam struktur dan pelaksanaan program perangkat lunak.
joshin4colours
18

Anda tidak boleh mengabaikan kesalahan secara diam-diam, karena program Anda dibangun di atas serangkaian tindakan yang secara implisit bergantung pada semua yang berjalan sebelum semuanya berjalan dengan benar. Jika ada yang tidak beres pada langkah 3, dan Anda mencoba melanjutkan ke langkah 4, langkah 4 akan dimulai berdasarkan asumsi yang tidak valid, yang membuatnya lebih cenderung menghasilkan kesalahan juga. (Dan jika Anda mengabaikannya juga, maka langkah 5 melempar kesalahan, dan hal-hal mulai turun dari sana.)

Masalahnya adalah, ketika kesalahan menumpuk, akhirnya Anda akan mengalami beberapa kesalahan begitu besar sehingga Anda tidak bisa mengabaikannya, karena itu akan terdiri dari sesuatu yang diberikan kepada pengguna, dan bahwa sesuatu akan sepenuhnya salah. Kemudian Anda memiliki pengguna mengeluh pada Anda tentang program Anda tidak berfungsi dengan benar, dan Anda harus memperbaikinya. Dan jika bagian "berikan sesuatu kepada pengguna" ada di langkah 28, dan Anda tidak tahu bahwa kesalahan asli yang menyebabkan semua kekacauan ini ada di langkah 3 karena Anda mengabaikan kesalahan di langkah 3, Anda akan memiliki heck of time men-debug masalah!

Di sisi lain, jika kesalahan pada langkah 3 membuat semuanya meledak di wajah pengguna, dan menghasilkan kesalahan yang mengatakan SOMETHING WENT BADLY WRONG IN STEP 3!(atau padanan teknisnya, jejak tumpukan,) maka hasilnya sama - pengguna mengeluh pada Anda tentang program tidak berfungsi dengan benar - tetapi kali ini Anda tahu persis di mana harus mulai mencari ketika Anda memperbaikinya .

EDIT: Menanggapi komentar, jika terjadi kesalahan yang Anda antisipasi dan tahu bagaimana mengatasinya, itu berbeda. Misalnya, dalam hal menerima pesan yang salah bentuk, itu bukan kesalahan program; itulah "input buruk yang diberikan pengguna yang gagal validasi." Respons yang sesuai di sana adalah memberi tahu pengguna bahwa dia memberi Anda input yang tidak valid, yang sepertinya Anda lakukan. Tidak perlu crash dan menghasilkan jejak stack dalam kasus seperti itu.

Mason Wheeler
sumber
Bagaimana kalau kembali ke posisi bagus terakhir yang diketahui, atau dalam hal loop penerima, untuk hanya menjatuhkan pesan itu dan pergi ke yang berikutnya.
Arlen Beiler
@ Arlen: Bahkan itu bisa menjadi ide yang buruk. Kesalahan terjadi karena sesuatu terjadi yang tidak Anda antisipasi ketika Anda mengodekannya . Ini berarti bahwa semua asumsi Anda telah keluar dari jendela. "Posisi bagus terakhir yang diketahui" itu mungkin tidak bagus lagi jika Anda setengah jalan memodifikasi beberapa struktur data dan sekarang itu dalam keadaan tidak konsisten, misalnya.
Mason Wheeler
Bagaimana jika saya mengharapkannya, seperti pesan yang rusak yang mencegah deserialisasi. Dalam beberapa kasus, saya telah membuat serial pengecualian dan mengirimkannya kembali. Di sana, lihat edit saya untuk pertanyaan.
Arlen Beiler
@ Arlen: Jika Anda benar-benar mengantisipasinya, dan Anda yakin bahwa Anda tahu apa efek dari kesalahan tersebut dan bahwa kesalahan itu terkandung dengan benar, maka itu berbeda. Kedengarannya bagi saya seperti Anda sedang menangani kesalahan dan merespons dengan tepat, bukan mengabaikannya. Saya berbicara tentang mengabaikan kesalahan yang tidak terduga , yang sepertinya Anda tanyakan.
Mason Wheeler
16

Ada opsi lain antara "meledakkan" dan "abaikan."

Jika kesalahan dapat diprediksi dan dihindari, ubah desain Anda atau refactor kode Anda untuk menghindarinya.

Jika kesalahan dapat diprediksi tetapi tidak dapat dihindari, tetapi Anda tahu apa yang harus dilakukan ketika itu terjadi, maka tangkap kesalahan tersebut dan tangani situasinya. Tapi hati-hati untuk menghindari penggunaan pengecualian sebagai kontrol aliran. Dan Anda mungkin ingin mencatat peringatan pada saat ini, dan mungkin memberi tahu pengguna jika ada beberapa tindakan yang mungkin mereka ambil untuk menghindari situasi ini di masa depan.

Jika kesalahan dapat diprediksi, tidak dapat dihindari, dan ketika itu terjadi tidak ada yang dapat Anda lakukan yang akan menjamin integritas data, maka Anda perlu mencatat kesalahan dan kembali ke keadaan aman (yang, seperti yang orang lain katakan, dapat berarti macet).

Jika kesalahan itu bukan sesuatu yang Anda antisipasi, maka Anda benar-benar tidak dapat memastikan bahwa Anda bahkan dapat kembali ke keadaan aman, jadi mungkin yang terbaik adalah login dan crash saja.

Sebagai aturan umum, jangan menangkap pengecualian yang tidak dapat Anda lakukan, kecuali Anda hanya berencana untuk login dan rethrow. Dan dalam kasus yang jarang terjadi di mana try-catch-ign tidak dapat dihindari, setidaknya tambahkan komentar di blok catch Anda untuk menjelaskan alasannya.

Lihat artikel penanganan pengecualian Eric Lippert yang sangat baik untuk saran lebih lanjut tentang kategorisasi dan penanganan pengecualian.

John M Gant
sumber
1
Jawaban terbaik sejauh ini, menurut saya.
thursdaysgeek
6

Inilah pandangan saya tentang pertanyaan:

Prinsip awal yang baik adalah gagal dengan cepat. Khususnya, Anda tidak boleh menulis kode penanganan kesalahan untuk setiap kegagalan yang Anda tidak tahu penyebab pastinya.

Setelah menerapkan prinsip ini, Anda dapat menambahkan kode pemulihan untuk kondisi kesalahan spesifik yang Anda temui. Anda juga dapat memperkenalkan beberapa "kondisi aman" untuk kembali. Membatalkan program sebagian besar aman, tetapi kadang-kadang Anda mungkin ingin kembali ke keadaan baik lain yang dikenal. Contohnya adalah bagaimana OS modern menangani program yang menyinggung. Itu hanya mematikan program, bukan seluruh OS.

Dengan gagal cepat dan perlahan-lahan mencakup lebih banyak dan lebih spesifik kondisi kesalahan Anda tidak pernah kompromi integritas data dan terus bergerak menuju program yang lebih stabil.

Menelan kesalahan, yaitu mencoba merencanakan kesalahan yang Anda tidak tahu penyebab pastinya dan karenanya tidak memiliki strategi pemulihan khusus, hanya mengarah pada peningkatan jumlah kode kesalahan-lompatan dan pengelakan dalam program Anda. Karena seseorang tidak dapat mempercayai bahwa data sebelumnya diproses dengan benar, Anda akan mulai melihat cek tersebar untuk data yang salah atau hilang. Kompleksitas siklomatik Anda akan lepas kendali dan Anda akan berakhir dengan bola lumpur yang besar.

Apakah Anda sadar atau tidak tentang kasus kegagalan itu kurang penting. Tetapi jika Anda misalnya berurusan dengan koneksi jaringan yang Anda tahu sejumlah negara kesalahan, tunda menambahkan penanganan kesalahan sampai Anda juga menambahkan kode pemulihan. Ini sejalan dengan prinsip-prinsip yang diuraikan di atas.

Alexander Torstling
sumber
6

Anda seharusnya tidak pernah diam - diam mengabaikan kesalahan. Dan terutama tidak dengan mengorbankan integritas data .

Program sedang mencoba melakukan sesuatu. Jika gagal, Anda harus menghadapi kenyataan dan melakukan sesuatu . Sesuatu itu tergantung pada banyak hal.

Pada akhirnya pengguna meminta program untuk melakukan sesuatu dan program harus memberi tahu mereka bahwa itu tidak berhasil. Ada banyak cara bagaimana itu bisa dilakukan. Mungkin berhenti segera, bahkan mungkin memutar kembali langkah-langkah yang sudah selesai atau di sisi lain itu dapat melanjutkan dan menyelesaikan semua langkah yang dapat dan kemudian memberitahu pengguna bahwa langkah-langkah ini berhasil dan yang lainnya gagal.

Cara mana yang Anda pilih tergantung pada seberapa dekat langkah-langkah terkait dan apakah kemungkinan kesalahan akan terulang untuk semua langkah di masa depan, yang pada gilirannya tergantung pada kesalahan yang tepat. Jika integritas data yang kuat diperlukan, Anda harus mengembalikan ke keadaan konsisten terakhir. Jika Anda hanya menyalin banyak file, Anda dapat melewati beberapa file dan memberi tahu pengguna pada akhirnya bahwa file-file itu tidak dapat disalin. Anda tidak boleh melewatkan file secara diam-diam dan tidak memberi tahu pengguna apa pun.

Edit iklan, satu-satunya perbedaan yang membuat Anda harus mempertimbangkan untuk mencoba lagi beberapa kali sebelum menyerah dan memberi tahu pengguna bahwa itu tidak berfungsi, karena jaringan cenderung memiliki kesalahan sementara yang tidak akan terulang kembali jika Anda coba lagi.

Jan Hudec
sumber
Apakah Anda benar-benar bermaksud memberi tanda tanya di akhir baris pertama?
CVn
@ MichaelKjörling: Tidak. Salin-tempel kesalahan (menyalin formulasi dari pertanyaan dan menyertakan tanda tanya tanpa sengaja).
Jan Hudec
4

Ada satu kelas kasus di mana mengabaikan kesalahan adalah hal yang benar untuk dilakukan: Ketika tidak ada yang bisa dilakukan tentang situasi dan ketika hasil yang buruk dan mungkin salah lebih baik daripada tidak ada hasil.

Kasus decoding aliran HDMI untuk keperluan tampilan adalah kasus seperti itu. Jika aliran buruk itu buruk, berteriak tentang itu tidak akan memperbaikinya secara ajaib. Anda melakukan apa yang Anda bisa untuk menampilkannya dan membiarkan pemirsa memutuskan apakah itu dapat ditoleransi atau tidak.

Loren Pechtel
sumber
1

Saya tidak percaya sebuah program harus diam-diam mengabaikan atau menyebabkan malapetaka setiap kali ada masalah.

Apa yang saya lakukan dengan perangkat lunak internal yang saya tulis untuk perusahaan saya ...

Itu tergantung pada kesalahan, katakanlah jika itu adalah fungsi kritis yang memasukkan data ke MySQL, perlu memberi tahu pengguna bahwa itu gagal. Penangan kesalahan harus mencoba mengumpulkan sebanyak mungkin informasi dan memberikan ide kepada pengguna tentang cara memperbaiki kesalahan sendiri sehingga mereka dapat menyimpan data. Saya juga suka memberikan cara diam-diam mengirim kami informasi di mana mereka mencoba menyimpan sehingga jika lebih buruk menjadi lebih buruk kita bisa memasukkannya secara manual setelah bug diperbaiki

Jika itu bukan fungsi kritis, sesuatu yang dapat membuat kesalahan dan tidak mempengaruhi hasil akhir dari apa yang mereka coba capai, saya mungkin tidak menunjukkan kepada mereka pesan kesalahan, tetapi minta mereka mengirim email yang secara otomatis memasukkannya ke dalam perangkat lunak pelacakan bug kami atau grup distribusi email yang memberi tahu semua programmer di perusahaan sehingga kami mengetahui kesalahannya, bahkan jika pengguna tidak. Ini memungkinkan kami untuk memperbaiki bagian belakang saat di ujung depan tidak ada yang tahu apa yang sedang terjadi.

Salah satu hal terbesar yang saya coba hindari adalah program macet setelah kesalahan - tidak dapat dipulihkan. Saya selalu mencoba memberi pengguna opsi untuk melanjutkan tanpa menutup aplikasi.

Saya percaya jika tidak ada yang tahu tentang bug - itu tidak akan pernah diperbaiki. Saya juga sangat percaya pada penanganan kesalahan yang memungkinkan aplikasi untuk terus berfungsi setelah bug ditemukan.

Jika kesalahan terkait dengan jaringan - mengapa tidak ada fungsi melakukan tes komunikasi jaringan sederhana sebelum menjalankan fungsi untuk menghindari kesalahan di tempat pertama? Kemudian hanya memberi tahu pengguna bahwa koneksi tidak tersedia, harap verifikasi internet Anda dll. Dll dan coba lagi?

Jeff
sumber
1
Saya pribadi melakukan banyak verifikasi sebelum menjalankan fungsi-fungsi penting untuk mencoba membatasi kesalahan yang saya tidak sepenuhnya mengerti dan tidak dapat memberikan penanganan kesalahan yang bermanfaat yang mengembalikan informasi terperinci. Itu tidak bekerja 100% dari waktu, tetapi saya tidak ingat kapan terakhir kali saya memiliki bug yang membuat saya kehilangan selama berjam-jam.
Jeff
1

Strategi saya sendiri adalah untuk membedakan antara kesalahan pengkodean (bug) dan kesalahan runtime , dan, sebisa mungkin, membuat kesalahan pengkodean sulit dibuat.

Bug perlu diperbaiki sesegera mungkin, sehingga pendekatan Desain dengan Kontrak sesuai. Di C ++, saya suka memeriksa semua prasyarat saya (input) dengan pernyataan di bagian atas fungsi untuk mendeteksi bug sesegera mungkin dan membuatnya mudah untuk melampirkan debugger dan memperbaiki bug. Jika pengembang atau penguji memilih untuk mencoba melanjutkan menjalankan program, kehilangan integritas data kemudian menjadi masalah mereka.

Dan temukan cara untuk mencegah bug sejak awal. Menjadi ketat dengan const-correctness dan memilih tipe data yang sesuai untuk data yang akan mereka pegang adalah dua cara untuk mempersulit membuat bug. Gagal-Cepat juga baik di luar kode kritis keselamatan yang membutuhkan cara untuk pulih.

Untuk kesalahan runtime yang mungkin terjadi dengan kode bebas bug, seperti kegagalan komunikasi serial atau jaringan atau file yang hilang atau rusak:

  1. Catat kesalahannya.
  2. (Opsional) Berusaha untuk mencoba kembali secara diam-diam atau memulihkan dari operasi.
  3. Jika operasi terus gagal atau tidak dapat dipulihkan, laporkan kesalahan tersebut kepada pengguna. Kemudian seperti di atas, pengguna dapat memutuskan apa yang harus dilakukan. Ingat Prinsip Terkecil Leasing , karena hilangnya integritas data mengejutkan bagi pengguna kecuali Anda sudah memperingatkan mereka sebelumnya.
snips-n-siput
sumber
0

Gagal adalah pilihan yang tepat ketika Anda memiliki alasan untuk berpikir bahwa keseluruhan kondisi program tidak stabil dan sesuatu yang buruk dapat terjadi jika Anda membiarkannya berjalan mulai sekarang. Agak "mengabaikan" itu (yaitu, seperti yang orang lain tunjukkan, mencatatnya di suatu tempat atau menampilkan pesan kesalahan kepada pengguna, lalu melanjutkan) tidak masalah ketika Anda tahu bahwa, tentu saja, operasi saat ini tidak dapat dilakukan, tetapi program dapat tetap berlari.

Fabien
sumber