Kasus spesifik saya di sini adalah bahwa pengguna dapat mengirimkan string ke dalam aplikasi, aplikasi mem-parsingnya dan menetapkannya ke objek terstruktur. Terkadang pengguna dapat mengetik sesuatu yang tidak valid. Misalnya, masukan mereka mungkin menggambarkan seseorang tetapi mereka mungkin mengatakan usia mereka adalah "apel". Perilaku yang benar dalam kasus itu adalah gulung balik transaksi dan untuk memberi tahu pengguna kesalahan terjadi dan mereka harus mencoba lagi. Mungkin ada persyaratan untuk melaporkan setiap kesalahan yang dapat kita temukan di input, bukan hanya yang pertama.
Dalam hal ini, saya berpendapat kita harus melempar pengecualian. Dia tidak setuju, mengatakan, "Pengecualian harus luar biasa: Diharapkan pengguna dapat memasukkan data yang tidak valid, jadi ini bukan kasus yang luar biasa" Saya tidak benar-benar tahu bagaimana memperdebatkan hal itu, karena menurut definisi kata, dia sepertinya benar.
Namun, menurut pemahaman saya inilah mengapa Pengecualian diciptakan sejak awal. Dulu Anda harus memeriksa hasilnya untuk melihat apakah ada kesalahan. Jika Anda gagal memeriksa, hal-hal buruk dapat terjadi tanpa Anda sadari.
Tanpa pengecualian setiap tingkat tumpukan perlu memeriksa hasil dari metode yang mereka panggil dan jika seorang programmer lupa untuk memeriksa di salah satu level ini, kode dapat secara tidak sengaja melanjutkan dan menyimpan data yang tidak valid (misalnya). Tampaknya lebih banyak kesalahan cenderung seperti itu.
Bagaimanapun, jangan ragu untuk memperbaiki apa pun yang saya katakan di sini. Pertanyaan utama saya adalah jika seseorang mengatakan Pengecualian harus luar biasa, bagaimana saya tahu jika kasus saya luar biasa?
sumber
Jawaban:
Pengecualian ditemukan untuk membantu mempermudah penanganan kesalahan dengan lebih sedikit kekacauan kode. Anda harus menggunakannya jika mereka membuat penanganan kesalahan lebih mudah dengan lebih sedikit kekacauan kode. Bisnis "pengecualian hanya untuk keadaan luar biasa" ini berasal dari saat penanganan pengecualian dianggap sebagai hit kinerja yang tidak dapat diterima. Itu tidak lagi terjadi pada sebagian besar kode, tetapi orang-orang masih menyemburkan aturan tanpa mengingat alasan di baliknya.
Khususnya di Jawa, yang mungkin merupakan bahasa yang paling mencintai pengecualian, Anda seharusnya tidak merasa bersalah menggunakan pengecualian ketika menyederhanakan kode Anda. Bahkan,
Integer
kelas Java sendiri tidak memiliki sarana untuk memeriksa apakah string adalah integer yang valid tanpa berpotensi melempar aNumberFormatException
.Selain itu, meskipun Anda tidak bisa hanya mengandalkan validasi UI, perlu diingat jika UI Anda dirancang dengan benar, seperti menggunakan pemintal untuk memasukkan nilai numerik pendek, maka nilai non-numerik yang membuatnya menjadi ujung belakang benar-benar akan menjadi kondisi luar biasa.
sumber
throw new ...
. Atau, lempar pengecualian khusus, tempat fillInStackTrace () ditimpa. Maka Anda seharusnya tidak melihat penurunan kinerja, apalagi hit .if (foo() == ERROR) { return ERROR; } else { // continue }
di setiap level. Jika Anda melempar pengecualian yang tidak dicentang, tidak ada bising dan berlebihan "jika kesalahan pengembalian kesalahan". Juga, jika Anda meneruskan fungsi sebagai argumen, menggunakan kode kesalahan dapat mengubah tanda tangan fungsi menjadi tipe yang tidak kompatibel, meskipun kesalahan mungkin tidak terjadi.Kapan pengecualian harus dilemparkan? Ketika datang ke kode, saya pikir penjelasan berikut ini sangat membantu:
Pengecualian adalah ketika seorang anggota gagal menyelesaikan tugas yang seharusnya dia lakukan sebagaimana ditunjukkan oleh namanya . (Jeffry Richter, CLR via C #)
Mengapa ini membantu? Ini menunjukkan bahwa itu tergantung pada konteks ketika sesuatu harus ditangani sebagai pengecualian atau tidak. Pada tingkat panggilan metode, konteksnya diberikan oleh (a) nama, (b) tanda tangan metode dan (b) kode klien, yang menggunakan atau diharapkan untuk menggunakan metode tersebut.
Untuk menjawab pertanyaan Anda, Anda harus melihat pada kode, di mana input pengguna diproses. Mungkin terlihat seperti ini:
Apakah nama metode menyarankan agar beberapa validasi dilakukan? Tidak. Dalam hal ini, PersonData yang tidak valid harus memberikan pengecualian.
Misalkan kelas memiliki metode lain yang terlihat seperti ini:
Apakah nama metode menyarankan agar beberapa validasi dilakukan? Iya. Dalam hal ini, PersonData yang tidak valid tidak boleh melempar pengecualian.
Untuk menyatukan semuanya, kedua metode menyarankan bahwa kode klien harus terlihat seperti ini:
Ketika tidak jelas apakah suatu metode harus mengeluarkan pengecualian, maka mungkin itu karena nama metode atau tanda tangan yang dipilih dengan buruk. Mungkin desain kelasnya tidak jelas. Terkadang Anda perlu memodifikasi desain kode untuk mendapatkan jawaban yang jelas atas pertanyaan apakah pengecualian harus dibuang atau tidak.
sumber
struct
disebut "ValidationResult" dan menyusun kode saya seperti yang Anda gambarkan.Validate
(mengembalikan False jika tidak valid) dan sekali selamaSave
(melempar pengecualian spesifik, yang terdokumentasi dengan baik jika tidak valid). Tentu saja, hasil validasi dapat di-cache di dalam objek, tetapi itu akan menambah kompleksitas tambahan, karena hasil validasi perlu dibatalkan pada perubahan.Validate()
disebut di dalamSave()
metode, dan detail spesifik dariValidationResult
bisa digunakan untuk membangun pesan yang sesuai untuk pengecualian.Atas argumen itu:
Setiap pengecualian yang Anda tangkap, Anda harus berharap karena, Anda memutuskan untuk menangkapnya. Dan dengan logika ini, Anda seharusnya tidak pernah melemparkan pengecualian yang Anda rencanakan untuk ditangkap.
Karenanya saya pikir "pengecualian harus luar biasa" adalah aturan praktis yang mengerikan.
Apa yang harus Anda lakukan tergantung pada bahasanya. Bahasa yang berbeda memiliki konvensi yang berbeda tentang kapan pengecualian harus dilemparkan. Python, misalnya, melempar pengecualian untuk semua dan ketika di Python, saya mengikutinya. C ++, di sisi lain, melempar pengecualian yang relatif sedikit, dan di sana saya mengikutinya. Anda dapat memperlakukan C ++ atau Java seperti Python dan memberikan pengecualian untuk semuanya, tetapi kerja Anda tidak sesuai dengan bagaimana bahasa itu sendiri diharapkan untuk digunakan.
Saya lebih suka pendekatan Python, tapi saya pikir itu ide yang buruk untuk memilih bahasa lain ke dalamnya.
sumber
"exceptions should be exceptional" is a terrible rule of thumb.
Dikatakan dengan baik! Itulah salah satu hal yang orang ulangi tanpa memikirkannya.Saya selalu memikirkan hal-hal seperti mengakses server basis data atau API web ketika memikirkan pengecualian. Anda mengharapkan server / API web berfungsi, tetapi dalam kasus luar biasa mungkin tidak (server down). Permintaan web mungkin cepat biasanya, tetapi dalam keadaan luar biasa (beban tinggi) mungkin waktu habis. Ini adalah sesuatu di luar kendali Anda.
Data input pengguna Anda berada dalam kendali Anda, karena Anda dapat memeriksa apa yang mereka kirimkan dan melakukan apa yang Anda suka. Dalam kasus Anda, saya akan memvalidasi input pengguna bahkan sebelum mencoba menyimpannya. Dan saya cenderung setuju bahwa pengguna yang menyediakan data yang tidak valid harusnya diharapkan, dan aplikasi Anda harus memperhitungkannya dengan memvalidasi input dan menyediakan pesan kesalahan yang ramah pengguna.
Yang mengatakan, saya menggunakan pengecualian di sebagian besar setter model domain saya, di mana seharusnya sama sekali tidak ada peluang data tidak valid masuk. Namun, ini adalah garis pertahanan terakhir, dan saya cenderung membangun form input dengan aturan validasi yang kaya , sehingga praktis tidak ada peluang memicu pengecualian model domain itu. Jadi ketika seorang setter mengharapkan satu hal, dan mendapatkan yang lain, itu adalah situasi yang luar biasa, yang seharusnya tidak terjadi dalam keadaan biasa.
EDIT (sesuatu yang perlu dipertimbangkan):
Saat mengirim data yang disediakan pengguna ke db, Anda tahu sebelumnya apa yang harus dan tidak harus Anda masukkan ke dalam tabel Anda. Ini berarti bahwa data dapat divalidasi terhadap beberapa format yang diharapkan. Ini adalah sesuatu yang bisa Anda kendalikan. Yang tidak bisa Anda kendalikan adalah server Anda gagal di tengah kueri Anda. Jadi, Anda tahu kueri itu ok dan data disaring / divalidasi, Anda mencoba kueri dan masih gagal, ini adalah situasi yang luar biasa.
Demikian pula dengan permintaan web, Anda tidak dapat mengetahui apakah permintaan akan habis, atau gagal terhubung sebelum Anda mencoba mengirimnya. Jadi ini juga menjamin pendekatan coba / tangkap, karena Anda tidak dapat menanyakan server apakah itu akan berfungsi beberapa milidetik nanti ketika Anda mengirim permintaan.
sumber
FileNotFoundException
ketika diberi input yang salah (misalnya nama file yang tidak ada). Itu adalah satu-satunya cara yang sah untuk mengancam kesalahan ini, apa lagi yang dapat Anda lakukan tanpa menghasilkan kode kesalahan kembali?Referensi
Dari Programmer Pragmatis:
Mereka melanjutkan untuk memeriksa contoh membuka file untuk dibaca, dan file tidak ada - haruskah itu menimbulkan pengecualian?
Kemudian, mereka membahas mengapa mereka memilih pendekatan ini:
Mengenai situasi Anda
Pertanyaan Anda menjadi "Haruskah kesalahan validasi meningkatkan pengecualian?" Jawabannya adalah itu tergantung pada di mana validasi terjadi.
Jika metode yang dimaksud adalah dalam bagian kode yang diasumsikan bahwa data input telah divalidasi, data input yang tidak valid harus memunculkan pengecualian; jika kode dirancang sedemikian rupa sehingga metode ini akan menerima input tepat yang dimasukkan oleh pengguna, diharapkan data yang tidak valid, dan pengecualian tidak boleh diajukan.
sumber
Ada banyak pemenuhan filosofis di sini, tetapi secara umum, kondisi luar biasa hanyalah kondisi yang tidak dapat atau tidak ingin Anda tangani (selain pembersihan, pelaporan kesalahan, dan sejenisnya) tanpa campur tangan pengguna. Dengan kata lain, itu adalah kondisi yang tidak dapat dipulihkan.
Jika Anda menyerahkan program path file, dengan maksud memproses file itu dengan cara tertentu, dan file yang ditentukan oleh path itu tidak ada, itu adalah kondisi yang luar biasa. Anda tidak dapat melakukan apa pun tentang itu dalam kode Anda, selain melaporkannya kepada pengguna dan mengizinkan mereka untuk menentukan jalur file yang berbeda.
sumber
Ada dua masalah yang harus Anda pertimbangkan:
Anda membahas satu masalah - sebut saja
Assigner
karena masalah ini adalah untuk menetapkan input ke objek terstruktur - dan Anda menyatakan kendala bahwa inputnya validantarmuka pengguna yang diimplementasikan dengan baik memiliki masalah tambahan: validasi input pengguna & umpan balik konstruktif tentang kesalahan (sebut saja bagian ini
Validator
)Dari sudut pandang
Assigner
komponen, melemparkan pengecualian benar-benar masuk akal, karena Anda telah menyatakan kendala yang telah dilanggar.Dari sudut pandang pengalaman pengguna , pengguna tidak boleh berbicara langsung dengan ini
Assigner
sejak awal. Mereka harus berbicara untuk itu melalui paraValidator
.Sekarang, dalam
Validator
, input pengguna yang tidak valid bukanlah kasus yang luar biasa, ini benar-benar kasus yang membuat Anda lebih tertarik. Jadi di sini pengecualian tidak akan sesuai, dan ini juga tempat Anda ingin mengidentifikasi semua kesalahan daripada menyerah pada yang pertama.Anda akan memperhatikan bahwa saya tidak menyebutkan bagaimana keprihatinan ini diterapkan. Sepertinya Anda sedang membicarakan tentang hal itu
Assigner
dan kolega Anda berbicara tentang gabunganValidator+Assigner
. Setelah Anda menyadari ada yang dua terpisah (atau dipisahkan) keprihatinan, setidaknya Anda bisa membicarakannya bijaksana.Untuk menanggapi komentar Renan, saya hanya berasumsi bahwa setelah Anda mengidentifikasi dua masalah Anda yang berbeda, jelas kasus apa yang harus dianggap luar biasa dalam setiap konteks.
Bahkan, jika tidak jelas apakah sesuatu harus dianggap luar biasa, saya berpendapat Anda mungkin belum selesai mengidentifikasi masalah independen dalam solusi Anda.
Saya kira itu membuat jawaban langsung untuk
tetap menyederhanakan sampai jelas . Ketika Anda memiliki setumpuk konsep sederhana yang Anda pahami dengan baik, Anda dapat beralasan dengan jelas untuk menyusunnya kembali menjadi kode, kelas, perpustakaan atau apa pun.
sumber
Orang lain telah menjawab dengan baik, tetapi masih di sini adalah jawaban singkat saya. Pengecualian adalah situasi di mana ada sesuatu di lingkungan yang salah, yang tidak dapat Anda kendalikan dan kode Anda tidak bisa maju sama sekali. Dalam hal ini Anda juga harus memberi tahu pengguna apa yang salah, mengapa Anda tidak bisa melangkah lebih jauh, dan apa resolusinya.
sumber
Saya tidak pernah menjadi penggemar berat saran bahwa Anda hanya harus melemparkan pengecualian dalam kasus-kasus yang luar biasa, sebagian karena itu tidak mengatakan apa-apa (seperti mengatakan bahwa Anda hanya harus makan makanan yang dapat dimakan), tetapi juga karena itu sangat subjektif, dan seringkali tidak jelas apa yang merupakan kasus luar biasa dan apa yang tidak.
Namun, ada alasan bagus untuk saran ini: melempar dan menangkap pengecualian lambat, dan jika Anda menjalankan kode Anda di debugger di Visual Studio dengan itu diatur untuk memberi tahu Anda setiap kali pengecualian dilemparkan, Anda bisa berakhir dipukul oleh puluhan jika tidak ratusan pesan jauh sebelum Anda sampai ke masalah.
Jadi sebagai aturan umum, jika:
maka kode Anda seharusnya tidak pernah melemparkan pengecualian, bahkan yang tertangkap kemudian. Untuk menjebak data yang tidak valid, Anda dapat menggunakan validator di tingkat UI atau kode seperti
Int32.TryParse()
di lapisan presentasi.Untuk hal lain, Anda harus tetap berpegang pada prinsip bahwa pengecualian berarti bahwa metode Anda tidak dapat melakukan apa yang dikatakan namanya. Secara umum itu bukan ide yang baik untuk menggunakan kode kembali untuk menunjukkan kegagalan (kecuali nama metode Anda dengan jelas menunjukkan bahwa ia melakukannya, misalnya
TryParse()
) karena dua alasan. Pertama, respons default untuk kode kesalahan adalah mengabaikan kondisi kesalahan dan melanjutkan tanpa memperhatikan; kedua, Anda dapat dengan mudah berakhir dengan beberapa metode menggunakan kode pengembalian dan metode lain menggunakan pengecualian, dan lupa yang mana. Saya bahkan telah melihat basis kode di mana dua implementasi yang berbeda dipertukarkan dari antarmuka yang sama mengambil pendekatan yang berbeda di sini.sumber
Pengecualian harus mewakili kondisi yang kemungkinan kode panggilan langsung tidak akan siap untuk ditangani, bahkan jika metode panggilan mungkin. Pertimbangkan, misalnya, kode yang membaca beberapa data dari file, dapat secara sah mengasumsikan bahwa file yang valid akan berakhir dengan catatan yang valid, dan tidak diharuskan untuk mengekstrak informasi apa pun dari catatan parsial.
Jika rutin baca-data tidak menggunakan pengecualian tetapi hanya melaporkan apakah pembacaan berhasil atau tidak, kode panggilan harus seperti:
dll. menghabiskan tiga baris kode untuk setiap karya yang bermanfaat. Sebaliknya, jika
readInteger
akan melempar pengecualian saat menemukan akhir file, dan jika pemanggil dapat meneruskan pengecualian, maka kode menjadi:Jauh lebih sederhana dan tampak lebih bersih, dengan penekanan yang jauh lebih besar pada kasus di mana segala sesuatu bekerja secara normal. Perhatikan bahwa dalam kasus-kasus di mana penelepon langsung akan mengharapkan untuk menangani suatu kondisi, metode yang mengembalikan kode kesalahan sering kali lebih bermanfaat daripada yang melempar pengecualian. Misalnya, untuk menjumlahkan semua bilangan bulat dalam file:
melawan
Kode yang meminta bilangan bulat mengharapkan bahwa salah satu dari panggilan itu akan gagal. Memiliki kode menggunakan loop tanpa akhir yang akan berjalan sampai itu terjadi jauh lebih elegan daripada menggunakan metode yang menunjukkan kegagalan melalui nilai kembali.
Karena kelas sering tidak tahu kondisi apa yang klien mereka akan atau tidak harapkan, sering membantu menawarkan dua versi metode yang bisa gagal dengan cara yang diharapkan oleh beberapa penelepon dan penelepon lainnya tidak. Melakukannya akan memungkinkan metode seperti itu digunakan secara bersih dengan kedua jenis penelepon. Perhatikan juga bahwa bahkan metode "coba" harus mengeluarkan pengecualian jika situasi muncul, penelepon mungkin tidak mengharapkannya. Misalnya,
tryReadInteger
tidak boleh melempar pengecualian jika menemui kondisi akhir file yang bersih (jika pemanggil tidak mengharapkan itu, pemanggil akan menggunakanreadInteger
). Di sisi lain, mungkin harus membuang pengecualian jika data tidak dapat dibaca karena mis. Memory stick yang mengandung itu dicabut. Meskipun peristiwa semacam itu harus selalu diakui sebagai suatu kemungkinan, tidak mungkin kode panggilan langsung akan disiapkan untuk melakukan sesuatu yang berguna sebagai tanggapan; seharusnya tidak dilaporkan dengan cara yang sama seperti kondisi akhir file.sumber
Hal terpenting dalam menulis perangkat lunak adalah membuatnya dapat dibaca. Semua pertimbangan lain bersifat sekunder, termasuk membuatnya efisien dan membuatnya benar. Jika itu dapat dibaca, sisanya dapat diurus dalam pemeliharaan, dan jika tidak dapat dibaca maka Anda lebih baik membuangnya saja. Karena itu, Anda harus melempar pengecualian ketika meningkatkan keterbacaan.
Saat Anda menulis beberapa algoritme, pikirkan saja orang di masa depan yang akan membacanya. Ketika Anda datang ke suatu tempat di mana ada potensi masalah, tanyakan pada diri Anda apakah pembaca ingin melihat bagaimana Anda menangani masalah itu sekarang , atau apakah pembaca lebih suka untuk melanjutkan dengan algoritma?
Saya suka memikirkan resep untuk kue coklat. Ketika ia memberi tahu Anda untuk menambahkan telur, ia memiliki pilihan: ia dapat mengasumsikan Anda memiliki telur dan melanjutkan resepnya, atau dapat memulai penjelasan tentang bagaimana Anda bisa mendapatkan telur jika Anda tidak memiliki telur. Itu bisa mengisi seluruh buku dengan teknik untuk berburu ayam liar, semua untuk membantu Anda membuat kue. Itu bagus, tetapi kebanyakan orang tidak akan mau membaca resep itu. Kebanyakan orang lebih suka berasumsi bahwa telur tersedia, dan melanjutkan resepnya. Itu panggilan penilaian yang perlu penulis buat saat menulis resep.
Tidak ada aturan yang dijamin tentang apa yang membuat pengecualian yang baik dan masalah apa yang harus ditangani segera, karena itu mengharuskan Anda membaca pikiran pembaca Anda. Yang terbaik yang pernah Anda lakukan adalah aturan praktis, dan "pengecualian hanya untuk keadaan luar biasa" adalah yang cukup bagus. Biasanya ketika pembaca membaca metode Anda, mereka mencari apa yang akan dilakukan 99% dari waktu itu, dan mereka lebih suka tidak memiliki kasus-kasus sudut yang aneh seperti menangani pengguna memasukkan input ilegal dan hal-hal lain yang hampir tidak pernah terjadi. Mereka ingin melihat aliran normal perangkat lunak Anda ditata secara langsung, satu per satu instruksi seolah-olah masalah tidak pernah terjadi.
sumber
Inilah sebabnya mengapa Anda tidak dapat melemparkan pengecualian di sini. Pengecualian segera mengganggu proses validasi. Jadi akan ada banyak penyelesaian untuk menyelesaikan ini.
Contoh buruk:
Metode validasi untuk
Dog
kelas menggunakan pengecualian:Bagaimana cara menyebutnya:
Masalahnya di sini adalah bahwa proses validasi, untuk mendapatkan semua kesalahan, harus melewati pengecualian yang sudah ditemukan. Hal di atas bisa berhasil, tetapi ini merupakan penyalahgunaan pengecualian . Jenis validasi yang Anda minta harus dilakukan sebelum basis data disentuh. Jadi tidak perlu memutar kembali apa pun. Dan, hasil validasi cenderung menjadi kesalahan validasi (semoga nol, meskipun).
Pendekatan yang lebih baik adalah:
Panggilan metode:
Metode validasi:
Mengapa? Ada banyak alasan, dan sebagian besar alasan telah ditunjukkan dalam tanggapan lainnya. Sederhananya: Jauh lebih mudah untuk dibaca dan dipahami oleh orang lain. Kedua, apakah Anda ingin menunjukkan jejak tumpukan pengguna untuk menjelaskan bahwa ia salah mengaturnya
dog
?Jika , selama komit dalam contoh kedua, masih muncul kesalahan , meskipun validator Anda memvalidasi
dog
dengan nol masalah, maka melemparkan pengecualian adalah hal yang benar . Seperti: Tidak ada koneksi basis data, entri basis data telah dimodifikasi oleh orang lain, atau sejenisnya.sumber