Baik penggunaan blok tangkap coba?

15

Saya selalu menemukan diri saya bergulat dengan ini ... mencoba menemukan keseimbangan yang tepat antara mencoba / menangkap dan kode tidak menjadi berantakan tab, kurung, dan pengecualian yang dilemparkan kembali ke tumpukan panggilan seperti kentang panas. Misalnya, saya memiliki aplikasi yang saya kembangkan saat ini yang menggunakan SQLite. Saya memiliki antarmuka Database yang abstrak panggilan SQLite, dan Model yang menerima hal-hal untuk masuk / keluar dari Database ... Jadi jika / ketika pengecualian SQLite terjadi, itu harus dilempar ke Model (yang menyebutnya ), siapa yang harus menyampaikannya kepada siapa pun yang disebut AddRecord / DeleteRecord / apa pun ...  

Saya penggemar pengecualian sebagai lawan mengembalikan kode kesalahan karena kode kesalahan dapat diabaikan, dilupakan, dll, sedangkan Pengecualian pada dasarnya harus ditangani (diberikan, saya bisa menangkap dan melanjutkan segera ...) Saya pasti ada cara yang lebih baik daripada apa yang saya lakukan saat ini.

Sunting:  Saya seharusnya mengatakan ini sedikit berbeda. Saya mengerti untuk melemparkan ulang sebagai tipe yang berbeda dan semacamnya, saya mengatakannya dengan buruk dan itu adalah kesalahan saya sendiri. Pertanyaan saya adalah ... bagaimana cara terbaik menjaga kode tetap bersih saat melakukannya? Itu hanya mulai terasa sangat berantakan bagi saya setelah beberapa saat.

coba tangkap
sumber
Bahasa pemrograman mana?
Apalala
2
C # saat ini, tetapi saya mencoba berpikir secara umum.
trycatch
C # tidak memaksa deklarasi pengecualian dilemparkan, yang membuatnya lebih mudah untuk menangani pengecualian yang masuk akal, dan menghindari programmer yang tergoda untuk menangkapnya tanpa benar-benar menanganinya. Anders Hejlsberg, desainer C #, membuat kasus terhadap pengecualian yang diperiksa dalam artikel ini artima.com/intv/handcuffs.html
Apalala

Jawaban:

14

Pikirkan itu dalam hal pengetikan yang kuat, bahkan jika Anda tidak menggunakan bahasa yang sangat diketik - jika metode Anda tidak dapat mengembalikan jenis yang Anda harapkan, itu harus membuang pengecualian.

Selain itu, alih-alih melempar SQLException sepenuhnya ke model (atau lebih buruk, UI), setiap lapisan harus menangkap pengecualian yang diketahui dan membungkus / mengubah / menggantinya dengan pengecualian yang cocok untuk lapisan itu:

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

Ini akan membantu membatasi jumlah pengecualian yang Anda cari di setiap lapisan dan membantu Anda mempertahankan sistem pengecualian terorganisir.

Nicole
sumber
Saya melakukan beberapa pengeditan, kata Q asli buruk .. pertanyaan saya adalah lebih lanjut tentang bagaimana menjaga kode bersih sambil menangani semua mencoba dan menangkap dan yang lainnya. Itu mulai terasa sangat berantakan ketika mencoba menangkap blok di mana-mana ...
trycatch
1
@ Apakah itu tidak mengganggu Anda bahwa UI atau Model Anda menangkap "SQLException"? Bagi saya, itu tidak terlalu relevan. Untuk masing-masing, kurasa.
Nicole
1
@ Ed, itu mungkin semacam OK dalam bahasa yang tidak memeriksa pengecualian, tetapi dalam bahasa dengan pengecualian yang diperiksa itu benar-benar jelek untuk memiliki throws SQLExceptionpada metode yang tidak menyiratkan bahwa SQL bahkan terlibat. Dan apa yang terjadi jika Anda memutuskan bahwa beberapa operasi harus pergi ke penyimpanan file? Sekarang Anda harus mendeklarasikan throws SQLException, IOException, dll. Itu akan keluar dari tangan.
Mike Daniels
1
@Ed ke pengguna akhir SQLException tidak banyak berarti, terutama jika sistem Anda dapat mendukung beberapa jenis kegigihan selain dari basis data relasional. Sebagai seorang programmer gui saya lebih suka harus berurusan dengan DataNotFoundException daripada seluruh rangkaian pengecualian tingkat rendah. Seringkali pengecualian untuk perpustakaan tingkat rendah hanyalah bagian dari kehidupan normal di atas, dalam hal ini mereka jauh lebih mudah ditangani ketika tingkat abstraksi mereka sesuai dengan aplikasi.
Newtopian
1
@Newtopian - Saya tidak pernah mengatakan menyajikan Pengecualian mentah kepada pengguna akhir. Untuk pengguna akhir, pesan sederhana 'Program telah berhenti berfungsi' sudah cukup saat masuk Exception di suatu tempat yang berguna untuk dijemput oleh dukungan. Dalam mode Debug, berguna untuk menampilkan Pengecualian. Anda tidak boleh peduli dengan setiap Pengecualian yang dimungkinkan yang dapat dilemparkan oleh API kecuali Anda memiliki penangan khusus untuk Pengecualian tersebut; biarkan penangkap Pengecualian Un-menangani Anda menangani semuanya.
Ed James
9

Pengecualian memungkinkan untuk menulis kode pembersih karena sebagian besar menangani kasus normal, dan kasus luar biasa dapat ditangani nanti, bahkan pada konteks yang berbeda.

Aturan untuk menangani perkecualian (penangkapan) adalah harus dilakukan berdasarkan konteks yang benar-benar dapat melakukan sesuatu. Tapi itu memiliki pengecualian:

Pengecualian harus ditangkap pada batas-batas modul (khususnya batas-batas lapisan) dan bahkan jika hanya untuk melampirkan mereka lemparan tingkat yang lebih tinggi yang memiliki arti bagi pemanggil. Setiap modul dan layer harus menyembunyikan detail implementasinya bahkan mengenai pengecualian (modul heap dapat membuang HeapFull tetapi tidak pernah ArrayIndexOutOfBounds).

Dalam contoh Anda, tidak mungkin lapisan atas dapat melakukan apa pun tentang pengecualian SQLite (jika mereka melakukannya, maka itu semua sangat digabungkan ke SQLite sehingga Anda tidak akan dapat mengubah lapisan data ke hal lain). Ada beberapa alasan yang dapat diramalkan untuk hal-hal seperti Tambah / Hapus / Perbarui gagal, dan beberapa di antaranya (perubahan yang tidak kompatibel dalam transaksi bersamaan) tidak mungkin untuk pulih dari bahkan di lapisan data / ketekunan (pelanggaran aturan integritas, fi). Lapisan persistensi harus menerjemahkan pengecualian ke sesuatu yang bermakna dalam istilah lapisan model sehingga lapisan atas dapat memutuskan apakah akan mencoba lagi atau gagal dengan anggun.

Apalala
sumber
Saya setuju, dan mengedit pertanyaan saya untuk mencerminkan bahwa di mana saya telah mengatakannya dengan buruk. Saya bermaksud agar ini menjadi lebih banyak pertanyaan tentang bagaimana menjaga usaha dan penangkapan agar tidak berantakan.
trycatch
Anda dapat menerapkan prinsip @ Ed James dan saya sebutkan di dalam lapisan atau modul. Alih-alih memanggil SQLite langsung dari semua tempat, memiliki beberapa metode / fungsi yang berbicara dengan SQLite dan pulih dari pengecualian, atau menerjemahkannya ke yang lebih umum. Jika transaksi melibatkan beberapa pertanyaan dan pembaruan, Anda tidak perlu menangani setiap kemungkinan pengecualian di masing-masing: satu try-catch luar dapat menerjemahkan pengecualian, dan yang dalam dapat menangani pembaruan sebagian dengan rollback. Anda juga dapat memindahkan pembaruan ke fungsinya sendiri untuk penanganan pengecualian yang lebih sederhana.
Apalala
1
hal ini akan lebih mudah dipahami dengan contoh kode ..
Klik Suara positif
Ini mungkin pertanyaan yang lebih cocok untuk stackoverflow dari awal.
Apalala
5

Sebagai aturan umum, Anda hanya harus menangkap Pengecualian spesifik (misalnya IOException), dan hanya jika Anda memiliki sesuatu yang spesifik untuk dilakukan setelah Anda menangkap Pengecualian.

Kalau tidak, sering kali lebih baik untuk membiarkan Pengecualian muncul ke permukaan sehingga mereka dapat diekspos dan ditangani. Beberapa orang menyebutnya gagal-cepat.

Anda harus memiliki semacam pawang di akar aplikasi Anda untuk menangkap Pengecualian yang tidak ditangani yang telah menggelembung dari bawah. Ini memberi Anda kesempatan untuk menyajikan, melaporkan, atau mengelola Pengecualian dengan cara yang sesuai.

Pengecualian Pembungkus berguna ketika Anda perlu melemparkan Pengecualian di sistem terdistribusi dan klien tidak memiliki definisi kesalahan sisi server.

Ed James
sumber
Menangkap dan membungkam pengecualian adalah hal yang mengerikan untuk dilakukan. Jika program rusak, seharusnya crash dengan pengecualian dan traceback. Seharusnya mengacaukan diam-diam mencatat sesuatu tetapi membuat data berantakan.
S.Lott
1
@ S.Lott Saya setuju bahwa orang tidak boleh membungkam pengecualian karena mereka menganggapnya menjengkelkan tetapi hanya menabrak aplikasi agak ekstrem. Ada banyak kasus di mana dimungkinkan untuk menangkap pengecualian dan mengatur ulang sistem dalam kondisi yang diketahui, dalam kasus seperti itu pengendali global semuanya cukup berguna. Namun, jika proses reset gagal dengan cara yang tidak bisa ditangani dengan aman maka ya, biarkan crash jauh lebih baik daripada menempelkan kepala seseorang di pasir.
Newtopian
2
sekali lagi itu semua tergantung pada apa yang Anda bangun tetapi saya biasanya tidak setuju dengan membiarkan mereka menggelembung karena itu menciptakan kebocoran abstraksi. Ketika saya menggunakan API, saya lebih suka melihatnya mengekspos pengecualian yang selaras dengan model API. Saya juga benci kejutan jadi saya benci ketika API membiarkan pengecualian terkait penerapannya mengintip tanpa pemberitahuan. Jika saya tidak tahu apa yang akan terjadi pada saya, bagaimana mungkin saya bereaksi! Saya mendapati diri saya terlalu sering menggunakan jaring penangkap yang jauh lebih lebar untuk mencegah kejutan agar tidak menabrak aplikasi saya tanpa pemberitahuan sebelumnya.
Newtopian
@Newtopian: Pengecualian "Pembungkus" dan "Menulis ulang" mengurangi kebocoran abstraksi. Mereka masih menggembung dengan tepat. "Jika aku tidak tahu apa yang akan terjadi padaku bagaimana aku bisa bereaksi! Aku mendapati diriku terlalu sering menggunakan jaring yang lebih luas" Adalah hal yang kami sarankan agar kamu berhenti lakukan. Anda tidak perlu menangkap semuanya. 80% dari waktu, hal yang benar adalah menangkap apa-apa. 20% dari waktu ada tanggapan yang berarti.
S.Lott
1
@Newtopian, saya pikir kita perlu membedakan antara pengecualian yang biasanya akan dilemparkan oleh objek dan karenanya harus dibungkus, dan pengecualian yang timbul karena bug dalam kode objek dan tidak boleh dibungkus.
Winston Ewert
4

Bayangkan Anda menulis kelas stack. Anda tidak menempatkan pengecualian dalam menangani kode di kelas, karena itu bisa menghasilkan pengecualian berikut.

  1. ArrayIndexError - dimunculkan ketika pengguna mencoba untuk pop dari tumpukan kosong
  2. NullPtrException - dimunculkan karena bug dalam implementasi menyebabkan upaya untuk referensi referensi nol

Pendekatan sederhana untuk membungkus pengecualian mungkin memutuskan untuk membungkus kedua pengecualian ini dalam kelas pengecualian StackError. Namun, ini benar-benar merindukan titik pembungkus pengecualian. Jika suatu objek melempar pengecualian tingkat rendah, itu berarti objek tersebut rusak. Namun, ada satu kasus di mana ini dapat diterima: ketika objek itu sebenarnya rusak.

Inti dari pembungkus pengecualian adalah bahwa objek harus memberikan pengecualian yang tepat untuk kesalahan normal. Tumpukan harus meningkatkan StackEmpty bukan ArrayIndexError saat muncul dari tumpukan kosong. Tujuannya bukan untuk menghindari melemparkan pengecualian lain jika objek atau kode rusak.

Apa yang kita benar-benar - ingin kita hindari adalah menangkap pengecualian tingkat rendah yang telah melewati objek tingkat tinggi. Kelas stack yang melempar ArrayIndexError ketika muncul dari tumpukan kosong adalah masalah kecil. Jika Anda benar-benar menangkap ArrayIndexError maka kami memiliki masalah serius. Propogasi kesalahan tingkat rendah adalah dosa yang jauh lebih serius daripada menangkapnya.

Untuk membawa ini kembali ke contoh Anda tentang SQLException: mengapa Anda mendapatkan persepsi SQLE? Salah satu alasannya adalah karena Anda melewati kueri yang tidak valid. Namun, jika lapisan akses data Anda menghasilkan kueri buruk, itu rusak. Seharusnya tidak berusaha untuk memperbaiki kerusakannya dalam pengecualian DataAccessFailure.

Namun, SQLException juga bisa muncul karena kehilangan koneksi ke database. Strategi saya pada titik itu adalah untuk menangkap pengecualian di garis pertahanan terakhir, melaporkan kepada pengguna bahwa konektivitas basis data hilang dan ditutup. Karena aplikasi kehilangan akses ke database, sebenarnya tidak banyak yang bisa dilakukan.

Saya tidak tahu seperti apa kode Anda. Tapi sepertinya Anda mungkin menerjemahkan semua pengecualian ke tingkat pengecualian yang lebih tinggi. Anda seharusnya hanya melakukan itu dalam jumlah kasus yang relatif kecil. Sebagian besar pengecualian tingkat yang lebih rendah menunjukkan bug dalam kode Anda. Menangkap dan membungkus mereka kembali adalah kontra-produktif.

Winston Ewert
sumber