Haruskah saya melemparkan pengecualian jika ada nilai yang berarti di luar rentang atau menanganinya sendiri?

42

Saya telah menulis sebuah struct yang mewakili koordinat lintang / bujur. Nilai mereka berkisar dari -180 hingga 180 untuk longtitude dan 90 hingga -90 untuk lattitudes.

Jika pengguna struct itu memberi saya nilai di luar rentang itu, saya punya 2 opsi:

  1. Lempar pengecualian (arg diluar jangkauan)
  2. Konversikan nilainya menjadi kendala

Karena koordinat -185 memiliki makna (sangat mudah dapat dikonversi ke +175 karena itu adalah koordinat kutub), saya dapat menerimanya dan mengubahnya.

Apakah lebih baik untuk melemparkan pengecualian untuk memberi tahu pengguna bahwa kodenya telah memberi saya nilai yang seharusnya tidak dimiliki?

Sunting: Juga saya tahu perbedaan antara lat / lng dan koordinat, tapi saya ingin menyederhanakan itu untuk diskusi yang lebih mudah - itu bukan ide yang paling cerdas

K. Gkinis
sumber
13
Haruskah pengguna diizinkan untuk memasukkan nilai di luar rentang? Jika jawabannya tidak, berikan pengecualian. Jika aturannya tidak seketat itu, lakukan konversi, tetapi nyatakan secara eksplisit dalam dokumentasi bahwa konversi dapat terjadi. Perlu diketahui juga bahwa dalam beberapa bahasa penanganan pengecualian cukup mahal.
Andy
C #, apakah itu penting? Ini tidak secara alami mendukung kendala jika ini yang Anda maksud.
K. Gkinis
2
Tampaknya cukup jelas bagi saya, kemudian, pendekatan yang benar adalah melarang pengguna untuk memasukkan apa pun di luar kisaran dan melemparkan pengecualian ketika mereka melakukannya (jika standar mengatakan nilai abs tidak boleh di atas 180, menempatkan nilai lebih besar dari itu adalah pelanggaran yang jelas). Ngomong-ngomong, C # sebenarnya adalah salah satu bahasa di mana pengecualian cukup mahal, jadi gunakan mereka benar-benar hanya dalam situasi, yang luar biasa, artinya tidak menangkapnya akan merusak aplikasi Anda, situasi seperti ini.
Andy
2
Saya cenderung menjauh dari membuat asumsi tentang apa yang pengguna 'maksudkan' dengan melewatkan nilai parameter tertentu, terutama yang tidak saya penuhi dengan kode saya. Kedengarannya seperti kasus serupa.
JᴀʏMᴇᴇ
2
Koordinat Web Mercator tidak dari -180 hingga 180 dan -90 hingga 90. Itu lintang / bujur (dan bahkan ada beberapa sistem koordinat untuk itu). Proyeksi Mercator biasanya dalam ratusan ribu atau jutaan dan memiliki unit "meter" (bahkan tidak seperti itu, karena panjang setiap unit mencakup peningkatan jarak darat nyata saat Anda mendekati kutub), bukan derajat. Bahkan dalam hal derajat, itu dibatasi hingga ± 85,051129 derajat karena proyeksi menjadi sangat lebar di kutub. (Saya sudah mengirim suntingan yang mengoreksi ini, karena ini bukan inti dari pertanyaan.)
jpmc26

Jawaban:

51

Jika inti dari pertanyaan Anda adalah ini ...

Jika beberapa kode klien melewati argumen yang nilainya tidak valid untuk hal yang pemodelan struktur data saya, haruskah saya menolak nilainya atau mengubahnya menjadi sesuatu yang masuk akal?

... maka jawaban umum saya adalah "tolak", karena ini akan membantu menarik perhatian ke bug potensial dalam kode klien yang sebenarnya menyebabkan nilai yang tidak valid muncul di program dan mencapai konstruktor Anda. Menarik perhatian pada bug pada umumnya adalah properti yang diinginkan di sebagian besar sistem, setidaknya selama pengembangan (kecuali itu adalah properti yang diinginkan sistem Anda untuk diatasi jika terjadi kesalahan).

Pertanyaannya adalah apakah Anda benar-benar menghadapi kasus itu .

  • Jika struktur data Anda dimaksudkan untuk memodelkan koordinat kutub secara umum, maka terima nilainya karena sudut di luar rentang -180 dan +180 tidak benar-benar tidak valid. Mereka benar-benar valid dan kebetulan selalu memiliki yang setara yang berada dalam kisaran -180 dan +180 (dan jika Anda ingin mengonversinya untuk menargetkan rentang itu, jangan ragu - kode klien biasanya tidak perlu peduli) .

  • Jika struktur data Anda secara eksplisit memodelkan koordinat Web Mercator (sesuai dengan pertanyaan dalam bentuk awalnya), maka yang terbaik adalah mengikuti ketentuan yang disebutkan dalam spesifikasi (yang saya tidak tahu, jadi saya tidak akan mengatakan apa-apa tentang itu) . Jika spesifikasi benda yang Anda modelkan mengatakan bahwa beberapa nilai tidak valid, tolaklah. Jika dikatakan bahwa mereka dapat diartikan sebagai sesuatu yang masuk akal (dan dengan demikian mereka benar-benar valid), terimalah.

The Mekanisme yang Anda gunakan untuk sinyal apakah nilai-nilai yang diterima atau tidak tergantung pada fitur bahasa Anda, filsafat umum dan persyaratan kinerja Anda. Jadi, Anda bisa melempar pengecualian (dalam konstruktor) atau mengembalikan versi nullable dari struct Anda (melalui metode statis yang memanggil konstruktor pribadi) atau mengembalikan boolean dan meneruskan struct Anda ke pemanggil sebagai outparameter (lagi melalui sebuah metode statis yang memanggil konstruktor pribadi), dan sebagainya.

Theodoros Chatzigiannakis
sumber
12
Garis bujur di luar rentang -180 hingga +180 mungkin harus dianggap dapat diterima, tetapi garis lintang di luar rentang -90 hingga +90 akan tampak tidak masuk akal. Begitu seseorang mencapai Kutub Utara atau Selatan, perjalanan lebih jauh ke utara atau selatan tidak ditentukan.
supercat
4
@supercat Saya cenderung setuju dengan Anda, tetapi karena saya tidak tahu nilai mana yang sebenarnya tidak valid dalam spesifikasi Web Mercator, saya berusaha untuk tidak menarik kesimpulan yang sulit. OP tahu domain masalah lebih baik daripada saya.
Theodoros Chatzigiannakis
1
@supercat: mulai dari mana meridian mencapai ekuator. melakukan perjalanan di sepanjang meridian menurut garis lintang. Jika garis lintang> 90 maka teruskan sepanjang lingkaran besar yang sama. Tidak masalah. Dokumentasikan dan serahkan sisanya pada klien.
kevin cline
4
@ supercat Ini lebih buruk dari itu. Dalam proyeksi Web Mercator, ± 90 sebenarnya diproyeksikan ke lebar tak terbatas. Jadi standar benar-benar memotongnya pada ± 85.051129. Juga, lat / long! = Koordinat web mercator. Koordinator Mercator menggunakan variasi meter, bukan derajat. (Ketika Anda semakin dekat ke kutub, setiap "meter" sebenarnya sesuai dengan bidang yang lebih besar dan lebih besar.) Koordinat OPs murni lat / panjang. Web Mercator tidak ada hubungannya dengan mereka, selain mereka mungkin ditampilkan di atas peta dasar Web Mercator dan beberapa perpustakaan spasial memproyeksikan di bawah tenda untuk mereka.
jpmc26
1
Saya harus mengatakan "setara dengan ± 85.051129," karena itu bukan koordinat yang sebenarnya.
jpmc26
10

Itu sangat tergantung. Tetapi Anda harus memutuskan untuk melakukan sesuatu dan mendokumentasikannya .

Satu-satunya hal yang pasti salah untuk kode Anda lakukan adalah lupa untuk mempertimbangkan bahwa input pengguna mungkin di luar kisaran yang diharapkan, dan menulis kode yang secara tidak sengaja memiliki beberapa perilaku. Karena beberapa orang akan membuat asumsi yang salah tentang bagaimana kode Anda berperilaku dan itu akan menyebabkan bug, sementara yang lain akan berakhir tergantung pada perilaku kode Anda secara tidak sengaja memiliki (bahkan jika perilaku itu benar-benar gila) dan sehingga Anda akan menyebabkan lebih banyak bug saat nanti Anda memperbaiki masalahnya.

Dalam hal ini saya bisa melihat argumennya. Jika seseorang bepergian +10 derajat dari 175 derajat, mereka akan berakhir di -175. Jika Anda selalu menormalkan input pengguna dan memperlakukan 185 sebagai setara dengan -175 maka kode klien tidak dapat melakukan hal yang salah ketika menambahkan 10 derajat; selalu memiliki efek yang tepat. Jika Anda memperlakukan 185 sebagai kesalahan yang Anda paksa setiap kasus di mana kode klien menambahkan derajat relatif untuk dimasukkan ke dalam logika normalisasi (atau setidaknya ingat untuk memanggil prosedur normalisasi Anda), Anda sebenarnya akan menyebabkanbug (meskipun mudah-mudahan mudah untuk menangkap yang akan cepat tergencet). Tetapi jika angka bujur dimasukkan oleh pengguna, ditulis secara harfiah dalam program, atau dihitung melalui beberapa prosedur yang dimaksudkan untuk selalu berada di [-180, 180), maka nilai di luar rentang tersebut sangat mungkin mengindikasikan kesalahan, jadi "sangat membantu "Mengubahnya bisa menyembunyikan masalah.

Cita-cita saya dalam hal ini mungkin akan mendefinisikan jenis yang mewakili domain yang benar. Gunakan tipe abstrak (jangan biarkan kode klien mengakses angka mentah di dalamnya), dan berikan pabrik normalisasi dan validasi (sehingga klien dapat melakukan tradeoff). Tetapi apa pun nilai dari jenis ini dibuat, 185 harus tidak dapat dibedakan dari -175 ketika dilihat melalui API publik Anda (tidak masalah apakah itu dikonversi pada konstruksi atau Anda memberikan kesetaraan, pengakses, dan operasi lain yang entah bagaimana mengabaikan perbedaannya) .

Ben
sumber
3

Jika tidak terlalu penting bagi Anda untuk memilih satu solusi, Anda bisa membiarkan pengguna yang memutuskan.

Mengingat struct Anda adalah objek nilai hanya baca dan dibuat oleh metode / konstruktor, Anda bisa memberikan dua kelebihan berdasarkan pilihan yang dimiliki pengguna:

  • Lempar pengecualian (arg diluar jangkauan)
  • Konversikan nilainya menjadi kendala

Juga tidak pernah membiarkan pengguna memiliki struct yang tidak valid untuk meneruskan ke metode Anda yang lain, lakukan dengan benar saat membuat.

Sunting: berdasarkan komentar, saya asumsikan Anda menggunakan c #.

Filipe Borges
sumber
Terima kasih atas masukannya! Saya akan memikirkannya, meskipun saya khawatir hal itu akan merusak tujuan pengecualian melempar konstruktor, jika orang bisa menghindarinya. Sangat menarik!
K. Gkinis
Jika Anda akan menggunakan ide ini, akan lebih baik untuk mendefinisikan antarmuka dan memiliki dua implementasi yang melempar atau mengonversi. Akan sulit untuk membebani konstruktor dengan cara yang masuk akal untuk bekerja seperti yang Anda gambarkan.
2
@Snowman: Ya, akan sulit untuk membebani konstruktor dengan tipe argumen yang sama, tetapi tidak akan sulit untuk memiliki dua metode statis dan konstruktor pribadi.
wchargin
1
@ K.Gkinis: Tujuan dari pengecualian melempar konstruktor bukanlah "memastikan aplikasi mati" —lebih lagi, klien selalu dapat catchpengecualian Anda. Seperti yang dikatakan orang lain, itu memungkinkan klien untuk membatasi diri jika diinginkan. Anda tidak benar-benar menghindari apa pun.
wchargin
Saran ini memperumit kode tanpa manfaat. Entah metode harus mengkonversi input yang tidak valid atau tidak seharusnya. Memiliki dua kelebihan membuat kode lebih kompleks tanpa menyelesaikan dilema.
JacquesB
0

Itu tergantung apakah input langsung dari pengguna melalui beberapa UI, atau dari sistem.

Input melalui UI

Ini adalah pertanyaan pengalaman pengguna bagaimana menangani input yang tidak valid. Saya tidak tahu tentang kasus spesifik Anda, tetapi secara umum ada beberapa opsi:

  • Peringatkan pengguna terhadap kesalahan dan minta pengguna untuk memperbaikinya sebelum melanjutkan (Paling umum)
  • Konversi secara otomatis ke rentang yang valid (jika mungkin), tetapi beri tahu pengguna untuk perubahan dan izinkan pengguna untuk memverifikasi sebelum melanjutkan.
  • Konversi secara diam-diam ke rentang yang valid dan lanjutkan.

Pilihannya tergantung pada harapan pengguna Anda dan seberapa penting data tersebut. Misalnya Google secara otomatis memperbaiki ejaan dalam kueri, tetapi ini berisiko rendah karena perubahan yang tidak membantu bukanlah masalah dan mudah diperbaiki (dan bahkan kemudian diperjelas pada halaman hasil bahwa kueri diubah). Di sisi lain, jika Anda memasukkan koordinat untuk rudal nuklir, Anda mungkin menginginkan validasi input yang lebih kaku dan tidak ada perbaikan diam untuk data yang tidak valid. Jadi tidak ada jawaban universal.

Yang paling penting, Anda harus mempertimbangkan apakah mengoreksi input bahkan memiliki manfaat bagi pengguna. Mengapa pengguna memasukkan data yang tidak valid? Sangat mudah untuk melihat bagaimana seseorang mungkin membuat kesalahan ejaan, tetapi mengapa ada orang yang memasukkan garis bujur -185? Jika pengguna benar-benar berarti +175 mereka mungkin akan mengetik +175. Saya pikir kemungkinan besar bahwa garis bujur yang tidak valid hanyalah kesalahan pengetikan, dan pengguna bermaksud -85 atau yang lainnya. Dalam hal ini, konversi secara diam-diam itu buruk dan tidak membantu . Pendekatan yang paling ramah pengguna untuk aplikasi Anda mungkin adalah untuk memperingatkan pengguna untuk nilai yang tidak valid, dan minta pengguna untuk memperbaikinya sendiri.

Input melalui API

Jika input dari sistem atau subsistem lain, tidak ada pertanyaan. Anda harus melempar pengecualian. Anda seharusnya tidak pernah diam mengonversi input yang tidak valid dari sistem lain, karena mungkin menutupi kesalahan di tempat lain dalam sistem. Jika input "dikoreksi" itu harus terjadi di lapisan UI, tidak lebih dalam ke sistem.

JacquesB
sumber
0

Anda harus melempar pengecualian.

Pada contoh yang Anda berikan, mengirim 185 dan mengonversinya menjadi -175, maka mungkin berguna dalam beberapa kasus untuk menyediakan fungsionalitas itu. Tetapi bagaimana jika penelepon mengirim 1 juta? Apakah mereka benar-benar bermaksud untuk mengubahnya? Tampaknya lebih mungkin itu adalah kesalahan. Jadi, jika Anda perlu melemparkan pengecualian untuk 1.000.000 tetapi tidak untuk 185, maka Anda harus membuat keputusan tentang ambang batas sewenang-wenang untuk melempar pengecualian. Ambang itu akan membuat Anda tersandung suatu saat karena beberapa aplikasi panggilan mengirim nilai di sekitar ambang tersebut.

Lebih baik membuang pengecualian untuk nilai di luar rentang.

brendan
sumber
-1

Opsi paling nyaman bagi pengembang adalah dukungan kompilasi waktu kesalahan di platform, untuk nilai di luar jangkauan. Dalam hal ini, rentang juga harus menjadi bagian dari tanda tangan metode, seperti jenis parameternya. Dengan cara yang sama pengguna API Anda tidak dapat meneruskan sebuah String jika tanda tangan metode Anda didefinisikan untuk mengambil bilangan bulat , pengguna seharusnya tidak dapat memberikan nilai tanpa memeriksa apakah nilainya berada dalam kisaran yang diberikan dalam tanda tangan metode. Jika tidak dicentang, ia harus mendapatkan kesalahan waktu kompilasi dan dengan demikian, kesalahan runtime dapat dihindari.

Namun saat ini, sangat sedikit kompiler / platform yang mendukung jenis pemeriksaan waktu kompilasi ini. Jadi, itu sakit kepala pengembang. Tetapi idealnya, metode Anda harus membuang pengecualian yang berarti untuk nilai yang tidak didukung dan mendokumentasikannya dengan jelas.

BTW, saya sangat suka model kesalahan yang diusulkan oleh Joe Duffy di sini .

Gulshan
sumber
Bagaimana Anda memberikan kesalahan waktu kompilasi untuk input pengguna?
JacquesB
@ JacquesB Kompiler akan mengeluarkan kesalahan jika input pengguna tidak dicentang berada dalam rentang setelah penyisipan, terlepas dari nilai aktual yang berada dalam kisaran.
Gulshan
Tetapi kemudian Anda masih harus memutuskan apakah Anda ingin menolak input atau mengonversi ke rentang yang valid, jadi ini tidak menjawab pertanyaan awal.
JacquesB
@ JacquesB Saya harus mengatakan ini, pada awalnya - saya selalu berpikir bahwa, OP sedang mengembangkan API dan UI dikembangkan oleh orang lain, mengkonsumsi API-nya. Saya salah tentang hal ini. Saya baru saja membaca pertanyaan itu lagi dan menyadari ini. Yang saya coba katakan adalah, validasi harus dilakukan pada konsumen API dan jika tidak, kompiler harus membuat kesalahan.
Gulshan
Jadi ... Anda perlu membuat kelas baru yang berisi input dan validasi. Annnnnd ketika Anda membangun objek kelas dari input mentah, apa yang terjadi? Melempar pengecualian? Diam-diam berlalu? Anda hanya memindahkan masalah.
djechlin
-1

Defaultnya adalah melempar pengecualian. Anda juga dapat mengizinkan opsi seperti strict=falsedan melakukan pemaksaan berdasarkan pada flag, di mana tentu saja strict=truedefault. Ini cukup umum:

  • Java DateFormatmendukung ringan .
  • Parson JSON Gson juga mendukung mode ringan .
  • Dll
Djechlin
sumber
Ini menyulitkan kode tanpa manfaat.
JacquesB
@ JacquesB melempar pengecualian secara default, atau mengizinkan strict=false?
djechlin
@ JacquesB Saya menambahkan beberapa contoh API yang mendukung mode ketat dan ringan, silakan lihat.
djechlin
-2

Bagi saya praktik terbaik adalah tidak pernah mengubah input pengguna. Pendekatan yang biasanya saya lakukan adalah memisahkan validasi dari eksekusi.

  • Memiliki kelas sederhana yang hanya menggunakan parameter yang diberikan.
  • Gunakan dekorator untuk memberikan lapisan validasi yang dapat diubah sesuka hati tanpa memengaruhi kelas eksekusi (atau menyuntikkan validator jika pendekatan ini terlalu sulit).
David A
sumber
"Pengguna" yang dimaksud pertanyaannya adalah pengembang menggunakan API, bukan pengguna akhir ...
Jay Elston