Konvensi untuk pengecualian atau kode kesalahan

118

Kemarin saya berdebat sengit dengan rekan kerja tentang apa yang akan menjadi metode pelaporan kesalahan yang disukai. Terutama kami membahas penggunaan pengecualian atau kode kesalahan untuk melaporkan kesalahan antara lapisan aplikasi atau modul.

Aturan apa yang Anda gunakan untuk memutuskan apakah Anda melempar pengecualian atau mengembalikan kode kesalahan untuk pelaporan kesalahan?

Jorge Ferreira
sumber

Jawaban:

81

Dalam hal-hal tingkat tinggi, pengecualian; dalam hal-hal tingkat rendah, kode kesalahan.

Perilaku default dari pengecualian adalah untuk melepas tumpukan dan menghentikan program, jika saya menulis skrip dan saya mencari kunci yang tidak ada dalam kamus, itu mungkin kesalahan, dan saya ingin program berhenti dan biarkan saya tahu semua tentang itu.

Namun, jika saya menulis sepotong kode yang harus saya ketahui perilakunya dalam setiap situasi yang memungkinkan, maka saya menginginkan kode kesalahan. Kalau tidak, saya harus mengetahui setiap pengecualian yang dapat dilemparkan oleh setiap baris dalam fungsi saya untuk mengetahui apa yang akan dilakukannya (Baca Pengecualian yang Membumikan Maskapai untuk mengetahui betapa rumitnya hal ini). Membosankan dan sulit untuk menulis kode yang bereaksi dengan tepat untuk setiap situasi (termasuk yang tidak menyenangkan), tetapi itu karena menulis kode bebas kesalahan itu membosankan dan sulit, bukan karena Anda mengirimkan kode kesalahan.

Baik Raymond Chen dan Joel telah membuat beberapa argumen fasih yang menentang penggunaan pengecualian untuk segala hal.

Tom Dunham
sumber
5
1 untuk menunjukkan bahwa konteksnya ada hubungannya dengan strategi penanganan kesalahan.
alx9r
2
@Tom, Poin bagus, tetapi pengecualian dijamin akan tertangkap. Bagaimana kami memastikan bahwa kode kesalahan tertangkap dan tidak diabaikan secara diam-diam karena kesalahan?
Pacerier
13
Jadi, satu-satunya argumen Anda terhadap pengecualian adalah bahwa sesuatu yang buruk dapat terjadi ketika Anda lupa untuk menangkap pengecualian namun Anda tidak mempertimbangkan situasi yang sama seperti lupa memeriksa kesalahan pada nilai yang dikembalikan? Belum lagi Anda mendapatkan pelacakan tumpukan ketika Anda lupa untuk menangkap pengecualian saat Anda tidak mendapatkan apa-apa ketika Anda lupa untuk memeriksa kode kesalahan.
Esailija
3
C ++ 17 memperkenalkan nodiscardatribut yang akan memberikan peringatan compiler jika nilai kembali dari suatu fungsi tidak disimpan. Membantu sedikit untuk menangkap pengecekan kode kesalahan yang terlupakan. Contoh: godbolt.org/g/6i6E0B
Zitrax
5
Komentar @ Esailija tepat di sini. Argumen melawan pengecualian di sini mengambil API berbasis kode kesalahan hipotetis di mana semua kode kesalahan didokumentasikan dan programmer hipotetis yang membaca dokumentasi, dengan benar mengidentifikasi semua kasus kesalahan yang mungkin secara logis dalam aplikasinya, dan menulis kode untuk menangani masing-masing. , lalu bandingkan skenario tersebut dengan pemrogram dan API berbasis pengecualian hipotetis di mana karena alasan tertentu salah satu langkah tersebut salah ... meskipun sama mudahnya (bisa dibilang lebih mudah ) untuk mendapatkan semua langkah tersebut dengan benar dalam API berbasis pengecualian.
Mark Amery
62

Saya biasanya lebih suka pengecualian, karena mereka memiliki informasi yang lebih kontekstual dan dapat menyampaikan (jika digunakan dengan benar) kesalahan kepada programmer dengan cara yang lebih jelas.

Di sisi lain, kode kesalahan lebih ringan daripada pengecualian tetapi lebih sulit untuk dipertahankan. Pemeriksaan kesalahan dapat dihilangkan secara tidak sengaja. Kode kesalahan lebih sulit untuk dipertahankan karena Anda harus menyimpan katalog dengan semua kode kesalahan dan kemudian mengaktifkan hasilnya untuk melihat kesalahan apa yang terjadi. Rentang kesalahan dapat membantu di sini, karena jika satu-satunya hal yang kita minati adalah apakah kita ada kesalahan atau tidak, lebih mudah untuk memeriksanya (misalnya, kode kesalahan HRESULT lebih besar atau sama dengan 0 adalah berhasil dan kurang dari nol adalah kegagalan). Mereka dapat secara tidak sengaja dihilangkan karena tidak ada pemaksaan terprogram yang akan diperiksa oleh pengembang untuk kode kesalahan. Di sisi lain, Anda tidak dapat mengabaikan pengecualian.

Untuk meringkas saya lebih suka pengecualian atas kode kesalahan di hampir semua situasi.

Jorge Ferreira
sumber
4
"kode kesalahan lebih ringan daripada pengecualian" tergantung pada apa yang Anda ukur dan bagaimana Anda mengukurnya. Sangat mudah untuk menghasilkan tes yang menunjukkan API berbasis pengecualian bisa jauh lebih cepat.
Mooing Duck
1
@ smink, Poin bagus, tapi bagaimana kita mengatasi overhead pengecualian? Kode kesalahan tidak hanya ringan, tetapi pada dasarnya tidak berbobot ; pengecualian bukan hanya kelas menengah, mereka adalah objek kelas berat yang berisi informasi tumpukan dan hal-hal lain yang tidak kami gunakan.
Pacerier
3
@Pacerier: Anda hanya mempertimbangkan pengujian yang pengecualiannya dilemparkan. Anda 100% benar bahwa menampilkan pengecualian C ++ secara signifikan lebih lambat daripada menampilkan kode kesalahan. Tangan ke bawah, tidak ada perdebatan. Dimana kita lakukan berbeda, adalah lain 99,999% dari kode. Dengan pengecualian, kita tidak harus memeriksa kode kesalahan pengembalian antara setiap pernyataan, membuat bahwa kode beberapa 1-50% lebih cepat (atau tidak, tergantung pada compiler). Yang berarti kode lengkap bisa lebih cepat, atau lebih lambat, bergantung sepenuhnya pada bagaimana kode ditulis dan seberapa sering pengecualian dilemparkan.
Mooing Duck
1
@Pacerier: Dengan tes buatan yang baru saja saya tulis, kode berbasis pengecualian secepat kode kesalahan di MSVC dan Clang, meskipun bukan GCC: coliru.stacked-crooked.com/a/e81694e5c508945b ( pengaturan waktu di bagian bawah). Tampaknya tanda GCC yang saya gunakan menghasilkan pengecualian yang sangat lambat dibandingkan dengan kompiler lain. Saya bias, jadi tolong kritik pengujian saya, dan silakan mencoba varian lain.
Mooing Duck
4
@Mooing Duck, Jika Anda menguji kode kesalahan dan pengecualian pada saat yang sama, maka Anda pasti sudah mengaktifkan pengecualian. Jika demikian, hasil Anda akan menunjukkan bahwa menggunakan kode kesalahan dengan overhead penanganan pengecualian tidak lebih lambat daripada hanya menggunakan pengecualian. Tes kode kesalahan perlu dijalankan dengan pengecualian dinonaktifkan untuk mendapatkan hasil yang berarti.
Mika Haarahiltunen
24

Saya lebih suka pengecualian karena

  • mereka mengganggu aliran logika
  • mereka mendapat manfaat dari hierarki kelas yang memberikan lebih banyak fitur / fungsionalitas
  • bila digunakan dengan benar dapat merepresentasikan berbagai kesalahan (misalnya, InvalidMethodCallException juga merupakan LogicException, karena keduanya terjadi saat ada bug dalam kode Anda yang seharusnya dapat dideteksi sebelum waktu proses), dan
  • mereka dapat digunakan untuk meningkatkan kesalahan (yaitu definisi kelas FileReadException kemudian dapat berisi kode untuk memeriksa apakah file tersebut ada, atau dikunci, dll)
JamShady
sumber
2
Poin keempat Anda tidak adil: Status kesalahan saat diubah menjadi objek, juga dapat berisi kode untuk memeriksa apakah file ada, atau terkunci, dll. Ini hanyalah variasi dari stackoverflow.com/a/3157182/632951
Pacerier
1
"mereka mengganggu aliran logika". Pengecualian memiliki efek kurang lebihgoto
peterchaula
22

Kode kesalahan dapat diabaikan (dan seringkali!) Oleh pemanggil fungsi Anda. Pengecualian setidaknya memaksa mereka untuk menangani kesalahan dengan cara tertentu. Bahkan jika versi mereka untuk menghadapinya adalah memiliki penangan tangkapan kosong (menghela nafas).

Maxam
sumber
17

Pengecualian atas kode kesalahan, tidak diragukan lagi. Anda mendapatkan banyak manfaat yang sama dari pengecualian seperti yang Anda dapatkan dengan kode kesalahan, tetapi juga lebih banyak lagi, tanpa kekurangan kode kesalahan. Satu-satunya pengecualian adalah bahwa itu sedikit lebih overhead; tetapi di zaman sekarang ini, overhead tersebut harus dianggap dapat diabaikan untuk hampir semua aplikasi.

Berikut beberapa artikel yang membahas, membandingkan, dan membedakan kedua teknik tersebut:

Ada beberapa tautan bagus di dalamnya yang dapat memberi Anda bacaan lebih lanjut.

Pistos
sumber
16

Saya tidak akan pernah mencampur dua model ... terlalu sulit untuk mengonversi dari satu ke yang lain saat Anda berpindah dari satu bagian tumpukan yang menggunakan kode kesalahan, ke bagian yang lebih tinggi yang menggunakan pengecualian.

Pengecualian adalah untuk "segala sesuatu yang menghentikan atau menghambat metode atau subrutin melakukan apa yang Anda minta" ... TIDAK memberikan pesan balik tentang penyimpangan atau keadaan yang tidak biasa, atau status sistem, dll. Gunakan nilai atau ref (atau keluar) parameter untuk itu.

Pengecualian memungkinkan metode untuk ditulis (dan digunakan) dengan semantik yang bergantung pada fungsi metode, yaitu metode yang mengembalikan objek Karyawan atau Daftar Karyawan dapat diketik untuk melakukan hal itu, dan Anda dapat menggunakannya dengan memanggil.

Employee EmpOfMonth = GetEmployeeOfTheMonth();

Dengan kode kesalahan, semua metode mengembalikan kode kesalahan, jadi, bagi mereka yang perlu mengembalikan sesuatu yang lain untuk digunakan oleh kode panggilan, Anda harus meneruskan variabel referensi untuk diisi dengan data itu, dan menguji nilai kembali untuk kode kesalahan, dan menanganinya, pada setiap pemanggilan fungsi atau metode.

Employee EmpOfMonth; 
if (getEmployeeOfTheMonth(ref EmpOfMonth) == ERROR)
    // code to Handle the error here

Jika Anda membuat kode sehingga setiap metode melakukan satu dan hanya satu hal sederhana, Anda harus memberikan pengecualian setiap kali metode tidak dapat mencapai tujuan yang diinginkan dari metode tersebut. Pengecualian jauh lebih kaya dan lebih mudah digunakan dengan cara ini daripada kode kesalahan. Kode Anda jauh lebih bersih - Alur standar dari jalur kode "normal" dapat dikhususkan hanya untuk kasus di mana metode IS dapat mencapai apa yang Anda inginkan ... Dan kemudian kode untuk membersihkan, atau menangani keadaan "luar biasa" ketika sesuatu yang buruk terjadi yang mencegah metode menyelesaikan dengan sukses dapat disingkirkan dari kode normal. Selain itu, jika Anda tidak dapat menangani pengecualian di tempat terjadinya, dan harus meneruskan tumpukan ke UI, (atau lebih buruk, melintasi kabel dari komponen tingkat menengah ke UI), maka dengan model pengecualian,

Charles Bretana
sumber
Jawaban yang bagus! Solusi di luar kotak tepat pada waktunya!
Cristian E.
11

Di masa lalu saya bergabung dengan kamp kode kesalahan (melakukan terlalu banyak pemrograman C). Tapi sekarang saya telah melihat cahaya.

Ya, pengecualian sedikit membebani sistem. Tetapi mereka menyederhanakan kode, mengurangi jumlah kesalahan (dan WTF).

Jadi gunakan pengecualian tetapi gunakan dengan bijak. Dan mereka akan menjadi temanmu.

Sebagai catatan tambahan. Saya telah belajar untuk mendokumentasikan pengecualian mana yang dapat dilemparkan dengan metode mana. Sayangnya ini tidak diperlukan oleh kebanyakan bahasa. Namun, hal itu meningkatkan peluang penanganan pengecualian yang tepat di tingkat yang tepat.

Toon Krijthe
sumber
1
yap C meninggalkan beberapa kebiasaan pada kita semua;)
Jorge Ferreira
11

Mungkin ada beberapa situasi di mana menggunakan pengecualian dengan cara yang bersih, jelas, dan benar tidak praktis, tetapi sebagian besar pengecualian waktu adalah pilihan yang jelas. Penanganan pengecualian manfaat terbesar memiliki lebih dari kode kesalahan adalah mengubah aliran eksekusi, yang penting karena dua alasan.

Ketika pengecualian terjadi, aplikasi tidak lagi mengikuti jalur eksekusi 'normal' itu. Alasan pertama mengapa ini begitu penting adalah bahwa kecuali pembuat kode berjalan dengan baik dan benar-benar keluar dari jalan mereka untuk menjadi buruk, program akan berhenti dan tidak terus melakukan hal-hal yang tidak terduga. Jika kode kesalahan tidak diperiksa dan tindakan yang tepat tidak diambil sebagai tanggapan atas kode kesalahan yang buruk, program akan terus melakukan apa yang dilakukannya dan siapa yang tahu apa hasil dari tindakan itu. Ada banyak situasi di mana membuat program melakukan 'apapun' bisa menjadi sangat mahal. Pertimbangkan program yang mengambil informasi kinerja untuk berbagai instrumen keuangan yang dijual perusahaan, dan mengirimkan informasi tersebut ke pialang / grosir. Jika terjadi kesalahan dan program terus berjalan, itu dapat mengirimkan data kinerja yang salah ke pialang dan grosir. Saya tidak tahu tentang orang lain, tetapi saya tidak ingin menjadi orang yang duduk di kantor VP menjelaskan mengapa kode saya menyebabkan perusahaan mendapatkan denda peraturan senilai 7 digit. Menyampaikan pesan kesalahan kepada pelanggan umumnya lebih baik daripada mengirimkan data yang salah yang mungkin terlihat 'nyata', dan situasi terakhir jauh lebih mudah untuk dihadapi dengan pendekatan yang jauh lebih tidak agresif seperti kode kesalahan.

Alasan kedua mengapa saya menyukai pengecualian dan pemutusan eksekusi normal mereka adalah karena itu membuatnya jauh, lebih mudah untuk menjaga logika 'hal-hal normal sedang terjadi' terpisah dari 'logika sesuatu yang salah'. Bagi saya, ini:

try {
    // Normal things are happening logic
catch (// A problem) {
    // Something went wrong logic
}

... lebih disukai daripada ini:

// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}
// Some normal stuff logic
if (errorCode means error) {
    // Some stuff went wrong logic
}

Ada hal-hal kecil lainnya tentang pengecualian yang bagus juga. Memiliki sekumpulan logika kondisional untuk melacak apakah salah satu metode yang dipanggil dalam suatu fungsi memiliki kode kesalahan yang dikembalikan, dan mengembalikan kode kesalahan itu lebih tinggi adalah banyak pelat boiler. Faktanya, banyak pelat boiler yang bisa salah. Saya memiliki lebih banyak kepercayaan pada sistem pengecualian dari sebagian besar bahasa daripada yang saya lakukan pada sarang tikus pernyataan if-else-if-else yang ditulis Fred 'Fresh-out-of-college', dan saya memiliki banyak hal yang lebih baik untuk dilakukan dengan waktu saya daripada meninjau kode kata sarang tikus.

Anjing
sumber
8

Anda harus menggunakan keduanya. Masalahnya adalah memutuskan kapan akan menggunakan masing-masing .

Ada beberapa skenario di mana pengecualian adalah pilihan yang jelas :

  1. Dalam beberapa situasi Anda tidak dapat melakukan apa pun dengan kode kesalahan , dan Anda hanya perlu menanganinya di tingkat atas dalam tumpukan panggilan , biasanya hanya mencatat kesalahan, menampilkan sesuatu kepada pengguna atau menutup program. Dalam kasus ini, kode kesalahan akan meminta Anda untuk menggelembung kode kesalahan secara manual tingkat demi tingkat yang jelas lebih mudah dilakukan dengan pengecualian. Intinya adalah ini untuk situasi yang tidak terduga dan tidak dapat ditangani .

  2. Namun tentang situasi 1 (di mana terjadi sesuatu yang tidak terduga dan tidak dapat ditangani, Anda tidak ingin mencatatnya), pengecualian dapat berguna karena Anda dapat menambahkan informasi kontekstual . Misalnya jika saya mendapatkan SqlException di pembantu data tingkat rendah saya, saya ingin menangkap kesalahan itu di tingkat rendah (di mana saya tahu perintah SQL yang menyebabkan kesalahan) sehingga saya dapat menangkap informasi itu dan menelusuri kembali dengan informasi tambahan . Harap perhatikan kata ajaib di sini: lempar ulang, dan jangan menelan . Aturan pertama penanganan pengecualian: jangan menelan pengecualian . Juga, perhatikan bahwa tangkapan dalam saya tidak perlu mencatat apa pun karena tangkapan luar akan memiliki seluruh jejak tumpukan dan dapat mencatatnya.

  3. Dalam beberapa situasi Anda memiliki urutan perintah, dan jika ada yang gagal Anda harus membersihkan / membuang sumber daya (*), apakah ini adalah situasi yang tidak dapat dipulihkan (yang harus dilempar) atau situasi yang dapat dipulihkan (dalam hal ini Anda dapat menangani secara lokal atau dalam kode penelepon tetapi Anda tidak memerlukan pengecualian). Jelas jauh lebih mudah untuk meletakkan semua perintah itu dalam sekali percobaan, daripada menguji kode kesalahan setelah setiap metode, dan membersihkan / membuang di blok terakhir. Harap dicatat bahwa jika Anda ingin kesalahan meluap (yang mungkin Anda inginkan), Anda bahkan tidak perlu menangkapnya - Anda cukup menggunakan akhirnya untuk pembersihan / pembuangan - Anda hanya boleh menggunakan tangkap / retrow jika Anda mau untuk menambahkan informasi kontekstual (lihat poin 2).

    Salah satu contohnya adalah urutan pernyataan SQL di dalam blok transaksi. Sekali lagi, ini juga merupakan situasi yang "tidak dapat ditangani", bahkan jika Anda memutuskan untuk mengetahuinya lebih awal (perlakukan secara lokal alih-alih membesar-besarkan) itu masih merupakan situasi yang fatal di mana hasil terbaik adalah membatalkan semuanya atau setidaknya menggugurkan bagian dari proses.
    (*) Ini seperti on error gotoyang kami gunakan di Visual Basic lama

  4. Dalam konstruktor, Anda hanya bisa melempar pengecualian.

Karena itu, dalam semua situasi lain di mana Anda mengembalikan beberapa informasi yang penelepon DAPAT / HARUS mengambil tindakan , menggunakan kode pengembalian mungkin merupakan alternatif yang lebih baik. Ini mencakup semua "error" yang diharapkan , karena mungkin error tersebut harus ditangani oleh pemanggil langsung, dan hampir tidak perlu dinaikkan terlalu banyak level di stack.

Tentu saja selalu mungkin untuk memperlakukan kesalahan yang diharapkan sebagai pengecualian, dan menangkap kemudian segera satu tingkat di atas, dan itu juga mungkin untuk mencakup setiap baris kode dalam coba tangkap dan mengambil tindakan untuk setiap kesalahan yang mungkin terjadi. IMO, ini adalah desain yang buruk, tidak hanya karena jauh lebih bertele-tele, tetapi khususnya karena kemungkinan pengecualian yang mungkin dilemparkan tidak jelas tanpa membaca kode sumber - dan pengecualian dapat dilemparkan dari metode dalam apa pun, menciptakan gotos yang tidak terlihat . Mereka merusak struktur kode dengan membuat beberapa titik keluar tak terlihat yang membuat kode sulit dibaca dan diperiksa. Dengan kata lain, Anda tidak boleh menggunakan pengecualian sebagai kontrol aliran, karena akan sulit bagi orang lain untuk memahami dan memeliharanya. Bahkan bisa menjadi sulit untuk memahami semua aliran kode yang mungkin untuk pengujian.
Sekali lagi: untuk pembersihan / pembuangan yang benar, Anda dapat menggunakan try-akhirnya tanpa menangkap apa pun .

Kritik paling populer tentang kode pengembalian adalah bahwa "seseorang dapat mengabaikan kode kesalahan, tetapi dalam arti yang sama seseorang juga dapat menelan pengecualian. Penanganan pengecualian yang buruk mudah dilakukan di kedua metode. Tetapi menulis program berbasis kode kesalahan yang baik masih jauh lebih mudah daripada menulis program berbasis pengecualian . Dan jika seseorang dengan alasan apa pun memutuskan untuk mengabaikan semua kesalahan (yang lama on error resume next), Anda dapat dengan mudah melakukannya dengan kode yang dikembalikan dan Anda tidak dapat melakukannya tanpa banyak uji coba tangkap.

Kritik paling populer kedua tentang kode pengembalian adalah bahwa "sulit untuk meluap" - tetapi itu karena orang tidak memahami bahwa pengecualian adalah untuk situasi yang tidak dapat dipulihkan, sedangkan kode kesalahan tidak.

Memutuskan antara pengecualian dan kode kesalahan adalah area abu-abu. Bahkan mungkin saja Anda perlu mendapatkan kode kesalahan dari beberapa metode bisnis yang dapat digunakan kembali, dan kemudian Anda memutuskan untuk menggabungkannya menjadi pengecualian (mungkin menambahkan informasi) dan membiarkannya menggelembung. Tapi itu adalah kesalahan desain untuk berasumsi bahwa SEMUA kesalahan harus dilemparkan sebagai pengecualian.

Singkatnya:

  • Saya suka menggunakan pengecualian ketika saya mengalami situasi yang tidak terduga, di mana tidak banyak yang harus dilakukan, dan biasanya kami ingin membatalkan blok kode yang besar atau bahkan seluruh operasi atau program. Ini seperti yang lama "pada kesalahan goto".

  • Saya suka menggunakan kode pengembalian ketika saya mengharapkan situasi di mana kode penelepon dapat / harus mengambil tindakan. Ini mencakup sebagian besar metode bisnis, API, validasi, dan sebagainya.

Perbedaan antara kode pengecualian dan kesalahan ini adalah salah satu prinsip desain bahasa GO, yang menggunakan "panik" untuk situasi tak terduga yang fatal, sementara situasi biasa yang diharapkan dikembalikan sebagai kesalahan.

Namun tentang GO, ini juga memungkinkan beberapa nilai kembali , yang merupakan sesuatu yang sangat membantu dalam menggunakan kode pengembalian, karena Anda dapat secara bersamaan mengembalikan kesalahan dan hal lain. Di C # / Java kita dapat mencapainya tanpa parameter, Tuple, atau Generik (favorit saya), yang dikombinasikan dengan enum dapat memberikan kode kesalahan yang jelas kepada pemanggil:

public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
    ....
    return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");

    ...
    return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}

var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
    // do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
    order = result.Entity; // etc...

Jika saya menambahkan kemungkinan pengembalian baru dalam metode saya, saya bahkan dapat memeriksa semua pemanggil jika mereka mencakup nilai baru itu dalam pernyataan sakelar misalnya. Anda benar-benar tidak dapat melakukannya dengan pengecualian. Saat Anda menggunakan kode pengembalian, Anda biasanya akan mengetahui terlebih dahulu semua kemungkinan kesalahan, dan mengujinya. Dengan pengecualian, Anda biasanya tidak tahu apa yang mungkin terjadi. Membungkus enum di dalam pengecualian (bukan Generik) adalah alternatif (selama jelas jenis pengecualian yang akan dilemparkan setiap metode), tetapi IMO itu masih desain yang buruk.

drizin
sumber
4

Alasan saya adalah jika Anda menulis driver tingkat rendah yang benar-benar membutuhkan kinerja, gunakan kode kesalahan. Tetapi jika Anda menggunakan kode itu dalam aplikasi tingkat yang lebih tinggi dan dapat menangani sedikit overhead, maka bungkus kode itu dengan antarmuka yang memeriksa kode kesalahan tersebut dan memunculkan pengecualian.

Dalam semua kasus lainnya, pengecualian mungkin merupakan cara yang tepat.

Claudiu
sumber
4

Saya mungkin duduk di pagar di sini, tapi ...

  1. Itu tergantung bahasanya.
  2. Model mana pun yang Anda pilih, konsistenlah tentang cara Anda menggunakannya.

Di Python, penggunaan pengecualian adalah praktik standar, dan saya cukup senang untuk mendefinisikan pengecualian saya sendiri. Di C Anda tidak memiliki pengecualian sama sekali.

Dalam C ++ (setidaknya di STL), pengecualian biasanya hanya diberikan untuk kesalahan yang benar-benar luar biasa (saya sendiri sebenarnya tidak pernah melihatnya). Saya tidak melihat alasan untuk melakukan sesuatu yang berbeda dalam kode saya sendiri. Ya, mudah untuk mengabaikan nilai yang dikembalikan, tetapi C ++ juga tidak memaksa Anda untuk menangkap pengecualian. Saya pikir Anda hanya perlu membiasakan diri melakukannya.

Basis kode yang saya kerjakan sebagian besar adalah C ++ dan kami menggunakan kode kesalahan hampir di mana-mana, tetapi ada satu modul yang menimbulkan pengecualian untuk kesalahan apa pun, termasuk yang sangat tidak terkecuali, dan semua kode yang menggunakan modul itu cukup mengerikan. Tapi itu mungkin saja karena kami telah mencampurkan pengecualian dan kode kesalahan. Kode yang secara konsisten menggunakan kode kesalahan jauh lebih mudah untuk dikerjakan. Jika kode kami secara konsisten menggunakan pengecualian, mungkin itu tidak akan seburuk itu. Mencampur keduanya sepertinya tidak berhasil dengan baik.

jrb
sumber
4

Karena saya bekerja dengan C ++, dan memiliki RAII untuk membuatnya aman digunakan, saya menggunakan pengecualian hampir secara eksklusif. Ini menarik penanganan kesalahan dari aliran program normal dan membuat maksudnya lebih jelas.

Saya meninggalkan pengecualian untuk keadaan luar biasa. Jika saya mengharapkan bahwa kesalahan tertentu akan sering terjadi, saya akan memeriksa bahwa operasi akan berhasil sebelum menjalankannya, atau memanggil versi fungsi yang menggunakan kode kesalahan sebagai gantinya (Suka TryParse())

Gerhana
sumber
3

Tanda tangan metode harus menyampaikan kepada Anda apa yang dilakukan metode tersebut. Sesuatu seperti long errorCode = getErrorCode (); mungkin baik-baik saja, tapi long errorCode = fetchRecord (); membingungkan.

Paul Croarkin
sumber
3

Pendekatan saya adalah kita dapat menggunakan keduanya, yaitu kode Pengecualian dan Kesalahan pada saat yang bersamaan.

Saya terbiasa mendefinisikan beberapa jenis Pengecualian (mis: DataValidationException atau ProcessInterruptExcepion) dan di dalam setiap pengecualian, tentukan deskripsi yang lebih rinci dari setiap masalah.

Contoh Sederhana di Java:

public class DataValidationException extends Exception {


    private DataValidation error;

    /**
     * 
     */
    DataValidationException(DataValidation dataValidation) {
        super();
        this.error = dataValidation;
    }


}

enum DataValidation{

    TOO_SMALL(1,"The input is too small"),

    TOO_LARGE(2,"The input is too large");


    private DataValidation(int code, String input) {
        this.input = input;
        this.code = code;
    }

    private String input;

    private int code;

}

Dengan cara ini saya menggunakan Pengecualian untuk menentukan kesalahan kategori, dan kode kesalahan untuk menentukan info lebih rinci tentang masalah tersebut.

sakana
sumber
2
erm ... throw new DataValidationException("The input is too small")? Salah satu keuntungan dari pengecualian adalah memungkinkan adanya informasi rinci.
Eva
2

Pengecualian adalah untuk keadaan luar biasa - yaitu, saat mereka bukan bagian dari alur normal kode.

Cukup sah untuk mencampur Pengecualian dan kode kesalahan, di mana kode kesalahan mewakili status sesuatu, daripada kesalahan dalam menjalankan kode itu sendiri (misalnya memeriksa kode yang dikembalikan dari proses anak).

Tetapi ketika keadaan luar biasa terjadi, saya yakin Pengecualian adalah model yang paling ekspresif.

Ada kasus di mana Anda mungkin lebih suka, atau harus, menggunakan kode kesalahan sebagai pengganti Pengecualian, dan ini telah dibahas secara memadai (selain kendala lain yang jelas seperti dukungan kompilator).

Namun ke arah lain, menggunakan Pengecualian memungkinkan Anda untuk membangun abstraksi tingkat yang lebih tinggi untuk penanganan kesalahan Anda, yang dapat membuat kode Anda lebih ekspresif dan alami. Saya sangat merekomendasikan membaca artikel yang sangat bagus, namun diremehkan, oleh ahli C ++ Andrei Alexandrescu ini tentang subjek yang dia sebut, "Penegakan": http://www.ddj.com/cpp/184403864 . Meskipun ini adalah artikel C ++ prinsipnya secara umum dapat diterapkan, dan saya telah menerjemahkan konsep penegakan ke C # dengan cukup berhasil.

philsquared
sumber
2

Pertama, saya setuju dengan jawaban Tom bahwa untuk barang tingkat tinggi menggunakan pengecualian, dan untuk barang tingkat rendah menggunakan kode kesalahan, selama itu bukan Service Oriented Architecture (SOA).

Dalam SOA, di mana metode dapat dipanggil di mesin yang berbeda, pengecualian tidak boleh melewati kabel, sebagai gantinya, kami menggunakan tanggapan berhasil / gagal dengan struktur seperti di bawah ini (C #):

public class ServiceResponse
{
    public bool IsSuccess => string.IsNullOrEmpty(this.ErrorMessage);

    public string ErrorMessage { get; set; }
}

public class ServiceResponse<TResult> : ServiceResponse
{
    public TResult Result { get; set; }
}

Dan gunakan seperti ini:

public async Task<ServiceResponse<string>> GetUserName(Guid userId)
{
    var response = await this.GetUser(userId);
    if (!response.IsSuccess) return new ServiceResponse<string>
    {
        ErrorMessage = $"Failed to get user."
    };
    return new ServiceResponse<string>
    {
        Result = user.Name
    };
}

Ketika ini digunakan secara konsisten dalam respons layanan Anda, itu menciptakan pola yang sangat bagus dalam menangani keberhasilan / kegagalan dalam aplikasi. Hal ini memungkinkan penanganan kesalahan yang lebih mudah dalam panggilan asinkron di dalam layanan maupun di seluruh layanan.

orad
sumber
1

Saya lebih suka Pengecualian untuk semua kasus kesalahan, kecuali jika kegagalan adalah hasil bebas bug yang diharapkan dari fungsi yang mengembalikan tipe data primitif. Misalnya, menemukan indeks substring dalam string yang lebih besar biasanya akan mengembalikan -1 jika tidak ditemukan, alih-alih memunculkan NotFoundException.

Mengembalikan pointer tidak valid yang mungkin direferensikan (misalnya menyebabkan NullPointerException di Java) tidak dapat diterima.

Menggunakan beberapa kode kesalahan numerik yang berbeda (-1, -2) sebagai nilai yang dikembalikan untuk fungsi yang sama biasanya merupakan gaya yang buruk, karena klien mungkin melakukan pemeriksaan "== -1" alih-alih "<0".

Satu hal yang perlu diingat di sini adalah evolusi API dari waktu ke waktu. API yang baik memungkinkan untuk mengubah dan memperluas perilaku kegagalan dalam beberapa cara tanpa merusak klien. Misalnya jika penanganan kesalahan klien memeriksa 4 kasus kesalahan, dan Anda menambahkan nilai kesalahan kelima ke fungsi Anda, penangan klien mungkin tidak menguji ini dan merusak. Jika Anda memunculkan Pengecualian, ini biasanya akan mempermudah klien untuk bermigrasi ke versi perpustakaan yang lebih baru.

Hal lain yang perlu dipertimbangkan adalah saat bekerja dalam tim, di mana harus menarik garis yang jelas bagi semua pengembang untuk membuat keputusan seperti itu. Misalnya "Pengecualian untuk hal-hal tingkat tinggi, kode kesalahan untuk hal-hal tingkat rendah" sangat subjektif.

Dalam kasus apa pun, di mana lebih dari satu jenis kesalahan sepele dimungkinkan, kode sumber tidak boleh menggunakan literal numerik untuk mengembalikan kode kesalahan atau untuk menanganinya (kembalikan -7, jika x == -7 ...), tapi selalu dinamai konstanta (kembalikan NO_SUCH_FOO, jika x == NO_SUCH_FOO).

tkruse
sumber
1

Jika Anda bekerja di bawah proyek besar, Anda tidak dapat menggunakan hanya pengecualian atau hanya kode kesalahan. Dalam kasus yang berbeda, Anda harus menggunakan pendekatan yang berbeda.

Misalnya, Anda memutuskan untuk menggunakan pengecualian saja. Tapi begitu Anda memutuskan untuk menggunakan pemrosesan acara async. Ide yang buruk untuk menggunakan pengecualian untuk penanganan kesalahan dalam situasi ini. Tetapi menggunakan kode kesalahan di mana-mana dalam aplikasi itu membosankan.

Jadi pendapat saya bahwa itu normal untuk menggunakan pengecualian dan kode kesalahan secara bersamaan.

gomons
sumber
0

Untuk sebagian besar aplikasi, pengecualian lebih baik. Pengecualiannya adalah ketika perangkat lunak harus berkomunikasi dengan perangkat lain. Domain tempat saya bekerja adalah kontrol industri. Di sini kode kesalahan lebih disukai dan diharapkan. Jadi jawaban saya adalah itu tergantung pada situasinya.

Jim C
sumber
0

Saya pikir itu juga tergantung pada apakah Anda benar-benar membutuhkan informasi seperti pelacakan tumpukan dari hasil. Jika ya, Anda pasti memilih Exception yang menyediakan objek yang penuh dengan banyak informasi tentang masalah. Namun, jika Anda hanya tertarik dengan hasil dan tidak peduli mengapa hasil itu, maka gunakan kode kesalahan.

mis. Saat Anda memproses file dan menghadapi IOException, klien mungkin tertarik untuk mengetahui dari mana pemicunya, dalam membuka file atau mem-parsing file, dll. Jadi, lebih baik Anda mengembalikan IOException atau subkelas spesifiknya. Namun, skenario seperti Anda memiliki metode login dan Anda ingin tahu itu berhasil atau tidak, di sana Anda hanya mengembalikan boolean atau untuk menampilkan pesan yang benar, kembalikan kode kesalahan. Di sini Klien tidak tertarik untuk mengetahui bagian logika mana yang menyebabkan kode kesalahan itu. Dia hanya tahu jika Kredensial tidak valid atau kunci akun dll.

Kasus penggunaan lain yang dapat saya pikirkan adalah ketika data berjalan di jaringan. Metode jarak jauh Anda hanya dapat mengembalikan kode kesalahan alih-alih Pengecualian untuk meminimalkan transfer data.

ashah
sumber
0

Aturan umum saya adalah:

  • Hanya satu kesalahan yang dapat muncul dalam suatu fungsi: gunakan kode kesalahan (sebagai parameter fungsi)
  • Lebih dari satu kesalahan spesifik dapat muncul: lempar pengecualian
DEADBEEF
sumber
-1

Kode kesalahan juga tidak berfungsi ketika metode Anda mengembalikan apa pun selain nilai numerik ...

Omar Kooheji
sumber
3
Um, tidak. Lihat paradigma win32 GetLastError (). Saya tidak membelanya, hanya mengatakan bahwa Anda salah.
Tim
4
Sebenarnya masih banyak cara lain untuk melakukannya. Cara lain adalah mengembalikan objek yang berisi kode kesalahan dan nilai pengembalian sebenarnya. Cara lain adalah dengan melakukan passing referensi.
Pacerier