Cara modern untuk melakukan penanganan kesalahan ...


118

Saya telah merenungkan masalah ini untuk sementara waktu sekarang dan menemukan diri saya terus menemukan peringatan dan kontradiksi, jadi saya berharap seseorang dapat menghasilkan kesimpulan sebagai berikut:

Mendukung pengecualian atas kode kesalahan

Sejauh yang saya ketahui, dari bekerja di industri selama empat tahun, membaca buku dan blog, dll. Praktik terbaik saat ini untuk menangani kesalahan adalah dengan melemparkan pengecualian, daripada mengembalikan kode kesalahan (tidak harus kode kesalahan, tetapi tipe mewakili kesalahan).

Tapi - bagi saya ini tampaknya bertentangan ...

Pengodean ke antarmuka, bukan implementasi

Kami mengkode ke antarmuka atau abstraksi untuk mengurangi kopling. Kami tidak tahu, atau ingin tahu, tipe spesifik dan implementasi antarmuka. Jadi bagaimana kita bisa tahu pengecualian apa yang harus kita tangkap? Implementasinya bisa melempar 10 pengecualian berbeda, atau bisa melempar tidak ada. Ketika kita menangkap pengecualian tentu kita membuat asumsi tentang implementasi?

Kecuali - antarmuka memiliki ...

Spesifikasi pengecualian

Beberapa bahasa memungkinkan pengembang untuk menyatakan bahwa metode tertentu melempar pengecualian tertentu (Java misalnya, menggunakan throwskata kunci.) Dari sudut pandang kode panggilan ini tampak baik - kita tahu secara eksplisit pengecualian mana yang mungkin perlu kita tangkap.

Tapi - ini sepertinya menyarankan ...

Abstraksi bocor

Mengapa antarmuka harus menentukan pengecualian yang dapat dilemparkan? Bagaimana jika implementasinya tidak perlu melempar pengecualian, atau perlu melempar pengecualian lain? Tidak ada cara, pada tingkat antarmuka, untuk mengetahui pengecualian mana yang mungkin ingin diterapkan oleh suatu implementasi.

Begitu...

Untuk menyimpulkan

Mengapa pengecualian lebih disukai ketika mereka (di mata saya) bertentangan dengan praktik terbaik perangkat lunak? Dan, jika kode kesalahan sangat buruk (dan saya tidak perlu dijual berdasarkan sifat buruk dari kode kesalahan), apakah ada alternatif lain? Bagaimana keadaan saat ini (atau segera menjadi) seni untuk penanganan kesalahan yang memenuhi persyaratan praktik terbaik seperti diuraikan di atas, tetapi tidak bergantung pada kode panggilan memeriksa nilai kembali kode kesalahan?


12
Saya tidak mendapatkan argumen Anda tentang abstraksi yang bocor. Menentukan apa pengecualian metode tertentu mungkin membuang merupakan bagian dari spesifikasi antarmuka . Sama seperti jenis nilai pengembalian adalah bagian dari spesifikasi antarmuka. Pengecualian hanya tidak "keluar dari sisi kiri" dari fungsi, tetapi mereka masih bagian dari antarmuka.
menipu

@deceze Bagaimana sebuah antarmuka dapat menyatakan apa yang mungkin dilemparkan oleh implementasi? Ini dapat membuang semua pengecualian dalam sistem tipe! Dan banyak bahasa yang tidak mendukung spesifikasi pengecualian menunjukkan bahwa mereka cukup cerdik
RichK

1
Saya setuju bahwa mungkin sulit untuk mengelola semua pengecualian yang berbeda yang mungkin dilempar metode, jika itu sendiri menggunakan metode lain dan tidak menangkap pengecualian mereka secara internal. Karena itu, itu bukan kontradiksi.
menipu

1
Asumsi yang bocor di sini adalah bahwa kode dapat menangani pengecualian ketika lolos dari batas antarmuka. Itu sangat tidak mungkin, mengingat bahwa itu tidak tahu hampir cukup tentang apa yang sebenarnya salah. Yang ia tahu adalah ada yang tidak beres dan implementasi antarmuka tidak tahu cara menghadapinya. Peluangnya untuk melakukan pekerjaan yang lebih baik adalah buruk, di luar melaporkannya dan menghentikan program. Atau mengabaikannya jika antarmuka tidak penting untuk operasi program yang tepat. Detail implementasi yang tidak dapat dan tidak harus Anda enkode dalam kontrak antarmuka.
Hans Passant

Jawaban:


31

Pertama-tama, saya tidak setuju dengan pernyataan ini:

Mendukung pengecualian atas kode kesalahan

Ini tidak selalu terjadi: misalnya, lihat Objective-C (dengan kerangka kerja Foundation). Di sana NSError adalah cara yang lebih disukai untuk menangani kesalahan, meskipun ada apa yang oleh pengembang Java akan disebut pengecualian sejati: @try, @catch, @throw, kelas NSException, dll.

Namun memang benar bahwa banyak antarmuka bocor abstraksi mereka dengan pengecualian yang dilemparkan. Ini adalah keyakinan saya bahwa ini bukan kesalahan "pengecualian" - gaya penyebaran / penanganan kesalahan. Secara umum saya percaya saran terbaik tentang penanganan kesalahan adalah ini:

Menangani kesalahan / pengecualian pada level serendah mungkin, titik

Saya pikir jika seseorang berpegang pada aturan praktis itu, jumlah "kebocoran" dari abstraksi bisa sangat terbatas dan terkandung.

Tentang apakah pengecualian yang dilemparkan oleh metode harus menjadi bagian dari deklarasi, saya percaya mereka harus: mereka adalah bagian dari kontrak yang didefinisikan oleh antarmuka ini: Metode ini melakukan A, atau gagal dengan B atau C.

Sebagai contoh, jika sebuah kelas adalah Parser XML, bagian dari desainnya harus menunjukkan bahwa file XML yang disediakan benar-benar salah. Di Jawa, Anda biasanya melakukannya dengan mendeklarasikan pengecualian yang ingin Anda temui dan menambahkannya ke throwsbagian deklarasi metode. Di sisi lain, jika salah satu algoritma penguraian gagal, tidak ada alasan untuk melewatkan pengecualian di atas tanpa penanganan.

Semuanya bermuara pada satu hal: Desain antarmuka yang baik. Jika Anda mendesain antarmuka dengan cukup baik, tidak ada pengecualian yang dapat menghantui Anda. Kalau tidak, bukan hanya pengecualian yang akan mengganggu Anda.

Juga, saya pikir pencipta Jawa memiliki alasan keamanan yang sangat kuat untuk memasukkan pengecualian ke metode deklarasi / definisi.

Satu hal terakhir: Beberapa bahasa, Eiffel misalnya, memiliki mekanisme lain untuk penanganan kesalahan dan tidak termasuk kemampuan melempar. Di sana, 'pengecualian' jenis secara otomatis dinaikkan ketika kondisi akhir untuk rutin tidak puas.


12
+1 untuk meniadakan "Pengecualian nikmat atas kode kesalahan." Saya telah diajarkan bahwa pengecualian itu baik dan bagus, asalkan pengecualian itu sebenarnya, dan bukan aturannya. Memanggil metode yang melempar pengecualian untuk menentukan apakah suatu kondisi benar atau tidak merupakan praktik yang sangat buruk.
Neil

4
@ JoshuaDrake: Dia pasti salah. Pengecualian dan gotosangat berbeda. Sebagai contoh, pengecualian selalu mengarah ke arah yang sama - ke bawah tumpukan panggilan. Dan kedua, melindungi diri Anda dari pengecualian mendadak adalah praktik yang sama persis dengan KERING - misalnya, dalam C ++, jika Anda menggunakan RAII untuk memastikan pembersihan, maka memastikan pembersihan dalam semua kasus, tidak hanya pengecualian tetapi juga semua aliran kontrol normal. Ini jauh lebih dapat diandalkan. try/finallymencapai sesuatu yang agak mirip. Saat Anda menjamin pembersihan dengan benar, Anda tidak perlu mempertimbangkan pengecualian sebagai kasus khusus.
DeadMG

4
@JoshuaDrake Maaf, tapi Joel jauh dari sana. Pengecualian tidak sama dengan goto, Anda selalu naik setidaknya satu tingkat tumpukan panggilan. Dan apa yang Anda lakukan jika level di atas tidak dapat langsung menangani kode kesalahan? Kembalikan kode kesalahan lain? Bagian dari masalah dengan kesalahan tidak adalah bahwa mereka BISA diabaikan, mengarah ke masalah yang lebih buruk daripada melemparkan pengecualian.
Andy

25
-1 karena saya benar-benar tidak setuju dengan "Menangani kesalahan / pengecualian pada level serendah mungkin" - itu salah, titik. Menangani kesalahan / pengecualian pada level yang sesuai . Cukup sering itu adalah level yang jauh lebih tinggi, dan dalam hal itu, kode kesalahan sangat menyusahkan. Alasan untuk mendukung pengecualian daripada kode kesalahan adalah bahwa mereka memungkinkan Anda untuk memilih secara bebas di tingkat mana untuk menghadapinya tanpa memengaruhi tingkat di antaranya.
Michael Borgwardt

2
@Giorgio: lihat artima.com/intv/handcuffs.html - terutama halaman 2 dan 3.
Michael Borgwardt

27

Saya hanya ingin mencatat bahwa pengecualian dan kode kesalahan bukan satu-satunya cara untuk menangani kesalahan dan jalur kode alternatif.

Keluar dari pikiran, Anda dapat memiliki pendekatan seperti yang diambil oleh Haskell, di mana kesalahan dapat diisyaratkan melalui tipe data abstrak dengan beberapa konstruktor (mis. Enum yang dibedakan, atau null pointer, tetapi typesafe dan dengan kemungkinan menambahkan sintaksis fungsi gula atau penolong untuk membuat aliran kode terlihat bagus).

func x = do
    a <- operationThatMightFail 10
    b <- operationThatMightFail 20
    c <- operationThatMightFail 30
    return (a + b + c)

operationThatMightfail adalah fungsi yang mengembalikan nilai yang dibungkus dengan Maybe. Ia bekerja seperti pointer yang dapat dibatalkan, tetapi notasi menjamin bahwa semuanya bernilai nol jika ada a, b, atau c gagal. (dan kompiler melindungi Anda dari membuat NullPointerException yang tidak disengaja)

Kemungkinan lain adalah melewatkan objek penangan kesalahan sebagai argumen tambahan untuk setiap fungsi yang Anda panggil. Penangan kesalahan ini memiliki metode untuk setiap "pengecualian" yang memungkinkan yang dapat ditandai oleh fungsi tempat Anda meneruskannya, dan dapat digunakan oleh fungsi itu untuk menangani pengecualian di tempat terjadinya, tanpa harus memundurkan tumpukan melalui pengecualian.

LISP umum melakukan ini, dan membuatnya layak dengan memiliki dukungan sintaksis (argumen implisit) dan memiliki fungsi bawaan mengikuti protokol ini.


1
Itu sangat rapi. Terima kasih telah menjawab bagian tentang alternatif :)
RichK

1
BTW, pengecualian adalah salah satu bagian paling fungsional dari bahasa imperatif modern. Pengecualian membentuk monad. Pengecualian menggunakan pencocokan derai. Pengecualian membantu meniru gaya aplikatif tanpa benar-benar mengetahui apa Maybeitu.
9000

Saya sedang membaca buku tentang SML baru-baru ini. Disebutkan jenis opsi, pengecualian (dan lanjutan). Sarannya adalah untuk menggunakan jenis opsi ketika kasus yang tidak ditentukan diharapkan terjadi lebih sering, dan untuk menggunakan pengecualian ketika kasus yang tidak ditentukan terjadi sangat jarang.
Giorgio

8

Ya, pengecualian dapat menyebabkan abstraksi bocor. Tetapi apakah kode kesalahan bahkan tidak lebih buruk dalam hal ini?

Salah satu cara untuk mengatasi masalah ini adalah membuat antarmuka menentukan dengan tepat pengecualian mana yang dapat dilemparkan dalam keadaan apa dan menyatakan bahwa implementasi harus memetakan model pengecualian internal mereka untuk spesifikasi ini, dengan menangkap, mengubah, dan melemparkan kembali pengecualian jika perlu. Jika Anda menginginkan antarmuka "prefek", itulah caranya.

Dalam praktiknya, biasanya cukup untuk menentukan pengecualian yang secara logis merupakan bagian dari antarmuka dan yang mungkin ingin ditangkap dan dilakukan klien. Secara umum dipahami bahwa dapat ada pengecualian lain ketika kesalahan tingkat rendah terjadi atau bug bermanifestasi, dan yang hanya dapat ditangani oleh klien dengan menunjukkan pesan kesalahan dan / atau mematikan aplikasi. Setidaknya pengecualian masih dapat berisi informasi yang membantu mendiagnosis masalah.

Bahkan, dengan kode kesalahan, hal yang hampir sama akhirnya terjadi, hanya dengan cara yang lebih implisit, dan dengan kemungkinan informasi yang hilang lebih banyak dan aplikasi berakhir dalam keadaan yang tidak konsisten.


1
Saya tidak mengerti mengapa kode kesalahan dapat menyebabkan abstraksi yang bocor. Jika kode kesalahan dikembalikan dan bukan nilai pengembalian normal, dan perilaku ini dijelaskan dalam spesifikasi fungsi / metode, maka IMO tidak ada kebocoran. Atau apakah saya mengabaikan sesuatu?
Giorgio

Jika kode kesalahan khusus untuk implementasi sedangkan API seharusnya merupakan implementasi-agnostik, maka menentukan dan mengembalikan kode kesalahan bocor detail implementasi yang tidak diinginkan. Contoh umum: API pencatatan dengan implementasi berbasis file dan berbasis DB, di mana yang pertama mungkin memiliki kesalahan "disk penuh", dan yang terakhir kesalahan "koneksi DB ditolak oleh host".
Michael Borgwardt

Aku mengerti apa yang kamu maksud. Jika Anda ingin melaporkan kesalahan spesifik implementasi, maka API tidak dapat berupa agnostik implementasi (baik dengan kode kesalahan maupun dengan pengecualian). Saya kira satu-satunya solusi adalah mendefinisikan kode kesalahan implementasi-agnostik seperti "sumber daya tidak tersedia", atau untuk memutuskan bahwa API bukan implementasi-agnostik.
Giorgio

1
@Giorgio: Ya, melaporkan kesalahan khusus implementasi rumit dengan API agnostik implementasi. Namun, Anda dapat melakukannya dengan pengecualian, karena (tidak seperti kode kesalahan) mereka dapat memiliki beberapa bidang. Jadi, Anda dapat menggunakan jenis pengecualian untuk memberikan informasi kesalahan umum (ResourceMissingException), dan menyertakan kode / pesan kesalahan implementasi khusus sebagai bidang. Terbaik dari kedua dunia :-).
sleske

Dan BTW, itulah yang dilakukan java.lang.SQLException. Ini memiliki getSQLState(generik) dan getErrorCode(khusus vendor). Sekarang seandainya memiliki subclass yang tepat ...
sleske

5

Banyak hal bagus di sini, saya hanya ingin menambahkan bahwa kita semua harus waspada terhadap kode yang menggunakan pengecualian sebagai bagian dari aliran kontrol normal. Kadang-kadang orang masuk ke dalam perangkap itu di mana segala sesuatu yang bukan kasus biasa menjadi pengecualian. Saya bahkan telah melihat pengecualian yang digunakan sebagai kondisi terminasi loop.

Pengecualian berarti "sesuatu yang tidak bisa saya tangani di sini terjadi, perlu pergi ke orang lain untuk mencari tahu apa yang harus dilakukan." Pengguna mengetik input yang tidak valid bukanlah pengecualian (yang harus ditangani secara lokal oleh input dengan bertanya lagi, dll.).

Kasus degenerasi lain dari penggunaan pengecualian yang pernah saya lihat adalah orang-orang yang respons pertamanya adalah "melempar pengecualian." Ini hampir selalu dilakukan tanpa menulis tangkapan (rule of thumb: tulis tangkapan pertama, kemudian pernyataan melempar). Dalam aplikasi besar ini menjadi bermasalah ketika pengecualian yang tidak tertangkap muncul dari bagian bawah dan meledakkan program.

Saya bukan anti-pengecualian, tetapi mereka tampak seperti lajang beberapa tahun yang lalu: terlalu sering digunakan dan tidak tepat. Mereka sempurna untuk penggunaan yang dimaksudkan, tetapi kasus itu tidak seluas yang dipikirkan beberapa orang.


4

Abstraksi bocor

Mengapa antarmuka harus menentukan pengecualian yang dapat dilemparkan? Bagaimana jika implementasinya tidak perlu melempar pengecualian, atau perlu melempar pengecualian lain? Tidak ada cara, pada tingkat antarmuka, untuk mengetahui pengecualian mana yang mungkin ingin diterapkan oleh suatu implementasi.

Nggak. Spesifikasi pengecualian berada di keranjang yang sama dengan tipe pengembalian dan argumen - mereka adalah bagian dari antarmuka. Jika Anda tidak dapat memenuhi spesifikasi itu, maka jangan mengimplementasikan antarmuka. Jika Anda tidak pernah melempar, maka itu tidak masalah. Tidak ada yang bocor dalam menentukan pengecualian dalam antarmuka.

Kode kesalahan sangat buruk. Mereka mengerikan. Anda harus ingat secara manual untuk memeriksa dan menyebarkannya, setiap waktu, untuk setiap panggilan. Ini melanggar KERING, untuk memulai, dan secara besar-besaran meledakkan kode penanganan kesalahan Anda. Pengulangan ini adalah masalah yang jauh lebih besar daripada yang dihadapi oleh pengecualian. Anda tidak akan pernah dapat mengabaikan pengecualian, tetapi orang dapat dan secara diam-diam mengabaikan kode kembali - jelas merupakan hal yang buruk.


Kode kesalahan dapat lebih mudah digunakan jika Anda memiliki bentuk sintaksis gula atau metode pembantu yang baik, dan dalam beberapa bahasa Anda dapat membuat kompiler dan jaminan tipe sistem bahwa Anda tidak akan pernah lupa untuk menangani kode kesalahan. Adapun bagian antarmuka pengecualian, saya pikir dia berpikir tentang kekakuan terkenal dari pengecualian diperiksa Jawa. Sementara mereka tampak seperti ide yang sangat masuk akal pada pandangan pertama, mereka menyebabkan banyak masalah yang menyakitkan dalam praktik.
hugomg

@missingno: Itu karena, seperti biasa, Java memiliki implementasi yang buruk, bukan karena pengecualian yang diperiksa pada dasarnya buruk.
DeadMG

1
@missingno: Masalah apa yang bisa disebabkan oleh pengecualian yang diperiksa?
Giorgio

@Iorgio: Sangat sulit untuk memperkirakan setiap jenis pengecualian yang mungkin dilemparkan oleh suatu metode, karena hal ini perlu mempertimbangkan subkelas dan kode lain yang belum ditulis. Dalam praktiknya, orang berakhir dengan penyelesaian buruk yang membuang informasi, seperti menggunakan kembali kelas pengecualian sistem untuk semuanya atau harus sering menangkap dan melempar pengecualian dalam. Saya juga telah mendengar bahwa pengecualian yang diperiksa adalah rintangan besar ketika mereka mencoba menambahkan fungsi anonim ke bahasa.
hugomg

@missingno: AFAIK fungsi anonim di Jawa hanya gula sintaksis untuk kelas batin anonim dengan tepat satu metode, jadi saya tidak yakin saya mengerti mengapa mengecek pengecualian akan menjadi masalah (tapi saya akui saya tidak tahu banyak tentang topik). Ya, sulit untuk memperkirakan pengecualian apa yang akan dilempar metode, oleh karena itu IMO dapat berguna untuk memeriksa pengecualian, sehingga Anda tidak perlu menebak. Tentu saja Anda dapat menulis kode yang buruk untuk menanganinya, tetapi ini juga dapat Anda lakukan dengan pengecualian yang tidak dicentang. Namun, saya tahu bahwa perdebatannya cukup kompleks dan jujur ​​saya melihat pro & kontra di kedua sisi.
Giorgio

2

Well Exception handling dapat memiliki implementasi antarmuka sendiri. Tergantung pada jenis pengecualian yang dilemparkan, lakukan langkah-langkah yang diinginkan.

Solusi untuk masalah desain Anda adalah memiliki dua implementasi antarmuka / abstraksi. Satu untuk fungsi dan lainnya untuk penanganan pengecualian. Dan tergantung pada jenis Pengecualian yang ditangkap, panggil kelas jenis pengecualian yang sesuai.

Implementasi kode Kesalahan adalah cara ortodoks dalam menangani pengecualian. Ini seperti penggunaan string vs string builder.


4
dengan kata lain: implementasi melempar subclass dari pengecualian yang didefinisikan dalam api.
andrew cooke

2

Pengecualian IM-HO sangat harus dinilai berdasarkan kasus per kasus, karena dengan memutus aliran kontrol mereka akan meningkatkan kompleksitas aktual dan yang dirasakan dari kode Anda, dalam banyak kasus tidak perlu begitu. Mengesampingkan diskusi terkait dengan melempar pengecualian di dalam fungsi Anda - yang sebenarnya dapat meningkatkan aliran kontrol Anda, jika seseorang ingin melihat melempar pengecualian melalui batas panggilan, pertimbangkan hal berikut:

Mengizinkan callee untuk memutus aliran kontrol Anda mungkin tidak memberikan manfaat nyata, dan mungkin tidak ada cara yang berarti untuk menangani pengecualian. Sebagai contoh langsung, jika seseorang menerapkan pola yang Dapat Diobservasi (dalam bahasa seperti C # di mana Anda memiliki acara di mana-mana dan tidak ada yang eksplisit throwsdalam definisi), tidak ada alasan aktual untuk membiarkan Pengamat memutus aliran kontrol Anda jika crash, dan tidak ada cara yang berarti untuk menangani barang-barang mereka (tentu saja, tetangga yang baik tidak boleh membuang ketika mengamati, tetapi tidak ada yang sempurna).

Pengamatan di atas dapat diperluas ke antarmuka yang digabungkan secara longgar (seperti yang Anda tunjukkan); Saya pikir itu sebenarnya norma bahwa setelah merangkak 3-6 frame stack, pengecualian yang tidak tertangkap mungkin berakhir di bagian kode yang:

  • terlalu abstrak untuk menangani pengecualian dengan cara apa pun yang berarti, bahkan jika pengecualian itu sendiri dibatalkan;
  • sedang melakukan fungsi generik (tidak peduli tentang mengapa Anda gagal, seperti pompa pesan, atau yang dapat diamati);
  • spesifik, tetapi dengan tanggung jawab yang berbeda, dan itu benar-benar tidak perlu khawatir;

Mempertimbangkan hal di atas, mendekorasi antarmuka dengan throwssemantik hanya merupakan keuntungan fungsional marjinal, karena banyak penelepon melalui kontrak antarmuka hanya akan peduli jika Anda gagal, bukan mengapa.

Saya akan mengatakan itu kemudian menjadi masalah selera dan kenyamanan: fokus utama Anda dengan anggun memulihkan keadaan Anda baik di penelepon dan callee setelah "pengecualian", oleh karena itu, jika Anda memiliki banyak pengalaman dalam memindahkan kode kesalahan sekitar (datang dari latar belakang C), atau jika Anda bekerja di lingkungan di mana pengecualian dapat mengubah kejahatan (C ++), saya tidak percaya bahwa melemparkan barang-barang sangat penting untuk OOP yang bagus dan bersih sehingga Anda tidak dapat mengandalkan yang lama pola jika Anda tidak nyaman dengan itu. Terutama jika itu mengarah pada pemecahan SoC.

Dari perspektif teoretis, saya pikir cara SoC-halal dalam menangani pengecualian dapat diturunkan langsung dari pengamatan bahwa sebagian besar penelepon langsung hanya peduli bahwa Anda gagal, bukan mengapa. Melemparkan callee, seseorang yang sangat dekat di atas (2-3 frame) menangkap versi upcasted, dan pengecualian sebenarnya selalu tenggelam ke penangan kesalahan khusus (bahkan jika hanya melacak) - ini adalah tempat AOP akan berguna, karena penangan ini berguna cenderung horisontal.


1

Mendukung pengecualian atas kode kesalahan

  • Keduanya harus hidup berdampingan.

  • Kembalikan kode kesalahan saat Anda mengantisipasi perilaku tertentu.

  • Kembalikan pengecualian saat Anda tidak mengantisipasi beberapa perilaku.

  • Kode kesalahan biasanya dikaitkan dengan satu pesan, ketika jenis pengecualian tetap ada, tetapi pesan dapat bervariasi

  • Pengecualian memiliki jejak tumpukan, ketika kode kesalahan tidak. Saya tidak menggunakan kode kesalahan untuk men-debug sistem yang rusak.

Pengkodean ke antarmuka bukan implementasi

Ini mungkin khusus untuk JAWA, tetapi ketika saya mendeklarasikan antarmuka saya, saya tidak menentukan pengecualian apa yang mungkin dilemparkan oleh implementasi antarmuka itu, itu hanya tidak masuk akal.

Ketika kita menangkap pengecualian tentu kita membuat asumsi tentang implementasi?

Ini sepenuhnya terserah Anda. Anda dapat mencoba dan menangkap jenis pengecualian yang sangat spesifik dan kemudian menangkap yang lebih umum Exception. Mengapa tidak membiarkan pengecualian menyebarkan tumpukan dan kemudian menanganinya? Atau Anda dapat melihat pemrograman aspek di mana penanganan pengecualian menjadi aspek "pluggable".

Bagaimana jika implementasinya tidak perlu melempar pengecualian, atau perlu melempar pengecualian lain?

Saya tidak mengerti mengapa ini menjadi masalah bagi Anda. Ya, Anda mungkin memiliki satu implementasi yang tidak pernah gagal atau melempar pengecualian dan Anda mungkin memiliki implementasi yang berbeda yang selalu gagal dan melempar pengecualian. Jika itu masalahnya, maka jangan tentukan pengecualian pada antarmuka dan masalah Anda telah terpecahkan.

Apakah akan mengubah apa pun jika bukan pengecualian, implementasi Anda mengembalikan objek hasil? Objek ini akan berisi hasil dari tindakan Anda bersama dengan kesalahan / kegagalan jika ada. Anda kemudian dapat menginterogasi objek itu.


1

Abstraksi bocor

Mengapa antarmuka harus menentukan pengecualian yang dapat dilemparkan? Bagaimana jika implementasinya tidak perlu melempar pengecualian, atau perlu melempar pengecualian lain? Tidak ada cara, pada tingkat antarmuka, untuk mengetahui pengecualian mana yang mungkin ingin diterapkan oleh suatu implementasi.

Dalam pengalaman saya, kode yang menerima kesalahan (baik itu melalui pengecualian, kode kesalahan atau apa pun) biasanya tidak peduli untuk penyebab pasti kesalahan - itu akan bereaksi dengan cara yang sama untuk setiap kegagalan, kecuali untuk kemungkinan pelaporan error (baik itu dialog kesalahan atau semacam log); dan pelaporan ini akan dilakukan secara ortogonal terhadap kode yang disebut prosedur gagal. Misalnya, kode ini dapat meneruskan kesalahan ke beberapa kode lain yang tahu cara melaporkan kesalahan tertentu (misalnya memformat string pesan), mungkin melampirkan beberapa informasi konteks.

Tentu saja, dalam beberapa kasus yang diperlukan untuk melampirkan semantik khusus untuk kesalahan dan bereaksi secara berbeda berdasarkan mana kesalahan terjadi. Kasing seperti itu harus didokumentasikan dalam spesifikasi antarmuka. Namun, antarmuka mungkin masih berhak untuk melempar pengecualian lain tanpa arti khusus.


1

Saya menemukan bahwa pengecualian memungkinkan untuk menulis kode yang lebih terstruktur dan ringkas untuk pelaporan dan penanganan kesalahan: menggunakan kode kesalahan memerlukan memeriksa nilai kembali setelah setiap panggilan dan memutuskan apa yang harus dilakukan jika terjadi hasil yang tidak terduga.

Di sisi lain saya setuju bahwa pengecualian mengungkapkan detail implementasi yang harus disembunyikan ke kode yang meminta antarmuka. Karena tidak mungkin untuk mengetahui apriori mana potongan kode dapat melempar pengecualian mana (kecuali mereka dinyatakan dalam metode tanda tangan seperti di Jawa), dengan menggunakan pengecualian kami memperkenalkan dependensi implisit yang sangat kompleks antara berbagai bagian kode, yang merupakan bertentangan dengan prinsip meminimalkan ketergantungan.

Meringkas:

  • Saya pikir pengecualian memungkinkan kode yang lebih bersih dan pendekatan yang lebih agresif untuk pengujian dan debugging karena pengecualian yang tidak tertangkap jauh lebih terlihat dan sulit untuk diabaikan daripada kode kesalahan (gagal segera).
  • Di sisi lain, bug pengecualian yang tidak tertangkap yang tidak ditemukan selama pengujian dapat muncul di lingkungan produksi dalam bentuk kerusakan. Dalam keadaan tertentu perilaku ini tidak dapat diterima dan dalam hal ini saya pikir menggunakan kode kesalahan adalah pendekatan yang lebih kuat.

1
Saya tidak setuju. Ya, pengecualian yang tidak tertangkap dapat menyebabkan crash suatu aplikasi, tetapi kode kesalahan yang tidak dicentang juga dapat terjadi - jadi ini adalah pencucian. Jika Anda menggunakan pengecualian dengan benar, satu-satunya yang tidak tertangkap akan menjadi fatal (seperti OutOfMemory), dan untuk ini, menabrak segera adalah yang terbaik yang dapat Anda lakukan.
sleske

Kode kesalahan adalah bagian dari kontrak antara penelepon m1 dan callee m2: kode kesalahan yang mungkin didefinisikan dalam antarmuka m2 saja. Dengan pengecualian (kecuali jika Anda menggunakan Java dan mendeklarasikan semua pengecualian yang dilemparkan dalam tanda tangan metode), Anda memiliki kontrak implisit antara penelepon m1 dan semua metode yang dapat dipanggil dengan m2, secara rekursif. Jadi tentu saja itu adalah kesalahan untuk tidak memeriksa kode kesalahan yang dikembalikan, tetapi selalu mungkin untuk melakukannya. Di sisi lain, tidak selalu mungkin untuk memeriksa semua pengecualian yang dilemparkan oleh suatu metode, kecuali Anda tahu bagaimana itu diterapkan.
Giorgio

Pertama: Anda dapat memeriksa semua pengecualian - cukup lakukan "Penanganan pengecualian Pokemon" (harus menangkap semuanya - yaitu catch Exceptionatau bahkan Throwable, atau setara).
sleske

1
Dalam praktiknya, jika API dirancang dengan benar, itu akan menentukan semua pengecualian yang dapat ditangani klien secara bermakna - ini perlu ditangkap secara khusus. Ini adalah setara dengan kode kesalahan). Pengecualian lain berarti "kesalahan internal", dan aplikasi harus dimatikan, atau setidaknya dimatikan subsistem yang sesuai. Anda dapat menangkap ini jika Anda mau (lihat di atas), tetapi Anda biasanya harus membiarkannya menggelembung. "Gelembung" itu adalah keuntungan utama menggunakan pengecualian. Anda masih dapat menangkapnya lebih jauh, atau tidak, tergantung kebutuhan.
sleske

-1

Bias Benar.

Itu tidak bisa diabaikan, harus ditangani, itu benar-benar transparan. Dan JIKA Anda menggunakan jenis kesalahan tangan kiri yang benar itu menyampaikan semua informasi yang sama sebagai pengecualian java.

Kelemahan? Kode dengan penanganan kesalahan yang tepat terlihat menjijikkan (benar dari semua mekanisme).


ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 10 jawaban sebelumnya. Mengapa menabrak pertanyaan dua tahun dengan hal-hal seperti itu
nyamuk

Kecuali tidak ada seorang pun di sini yang menyebutkan hak bias juga. Hugomg berbicara tentang haskell, namun Mungkin adalah penangan kesalahan karena tidak meninggalkan penjelasan mengapa kesalahan terjadi, atau metode langsung yang dapat digunakan untuk memulihkan, dan panggilan balik adalah salah satu dosa terbesar dalam desain aliran kontrol. Dan pertanyaan ini muncul di google.
Keynan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.