Apakah Anda melempar argumentexception atau argumentnullexception dari metode pribadi?

20

Saya baru saja meninjau beberapa kode yang saya tulis beberapa waktu lalu, dan dapat melihat bahwa saya memiliki beberapa metode pribadi yang melempar argumentnullexceptions dan / atau argumentexions jika ada masalah dengan parameter metode.

Saya kira alasan saya adalah membantu aplikasi bukti di masa depan jika seseorang mencoba untuk "menyalahgunakan" metode di masa depan. Namun, mengingat ini adalah metode pribadi dan orang-orang yang cenderung memanggil metode ini dapat melihat komentar dan kode yang terkait, tidak perlu membuang ini. Jelas tidak ada salahnya memilikinya, meskipun itu menambah kekacauan.

Perasaan saya adalah bahwa pengecualian ini umumnya lebih bermanfaat pada sesuatu seperti API yang akan diekspos secara publik.

Tuan Moose
sumber

Jawaban:

22

Biasanya, untuk metode pribadi Anda tidak membuang pengecualian karena saat Anda menulisnya, pengembang seharusnya tahu bagaimana dan dari mana ia memanggil metode tersebut. Dengan demikian, variabel yang dikirimkan sebagai parameter ke metode pribadi harus diperiksa di luar metode, yaitu, sebelum memanggilnya. Melempar "InvalidArgumentException" dan pengecualian semacam itu dianggap praktik yang baik untuk metode publik (baik Anda menulis "API" atau tidak).

Untuk kasus-kasus di mana Anda ingin melempar "InvalidArgumentException" perlu disebutkan bahwa ada Assertkelas di Spring API untuk Java sejak versi 1.1.2. Sudah sangat membantu - setidaknya bagi saya - dalam menulis lebih sedikit kode untuk melakukan pemeriksaan.

Namun Anda dapat menggunakan "menegaskan" untuk memeriksa parameter dalam metode pribadi. Itu adalah salah satu tujuan mereka yang sebenarnya. Mereka lebih banyak alasan untuk menggunakannya, lihat tautan berikut yang juga menjelaskan secara menyeluruh kapan harus menggunakan pernyataan dan kapan harus menggunakan pengecualian. Pemberitahuan tidak dimasukkan dalam kode produksi dan kompiler menghapusnya secara default. Jadi itulah yang Anda cari: membantu pengembang, tidak terlihat oleh pengguna. Di Jawa Anda harus menggunakan bendera khusus ("-ea") untuk memberi tahu kompiler untuk mengaktifkan pernyataan. Anda dapat menganggap mereka sebagai teman "men-debug".

Inilah cara menggunakan pernyataan di:

Jalayn
sumber
1
Apache Commons juga memiliki kelas Validasi yang fungsinya mirip dengan kelas Spring's Assert
Rosa Richter
@ Cantido ya dan saya juga akan menambahkan bahwa kelas Prasyarat di Google Guava yang bekerja dengan cara yang sama. Terima kasih atas informasinya :-)
Jalayn
2

Seperti semua yang lain itu tergantung ....

Jika metode publik adalah pembungkus sederhana yang memanggil metode pribadi (di sepanjang garis metode kelebihan beban pribadi) maka mungkin masuk akal untuk melemparkan pengecualian dalam metode pribadi alih-alih memeriksa di masing-masing metode publik.

Secara umum jika tidak memenuhi definisi di atas maka saya biasanya tidak akan memeriksa argumen / melemparkan pengecualian pada metode pribadi. Meskipun ada kasus lain saya biasanya melakukan ini dalam metode pribadi sebelum melakukan beberapa operasi mahal yang bisa gagal sebagian jika argumen tidak valid.

Ken Henderson
sumber
2

Saya menyadari bahwa sementara pertanyaan tidak memiliki tag bahasa, itu mungkin secara implisit berbicara tentang "bahasa kopi". Tapi hanya demi kelengkapan, saya ingin menyebutkan konsensus yang agak menyimpang di dunia C ++.

Ada tiga hal yang biasanya diminati oleh programmer C ++:

  • Apakah ini memiliki nol overhead dalam build yang dioptimalkan? (Yaitu, bisakah itu "dikompilasi"?)
  • Bisakah saya menggunakannya untuk menjebak debugger tepat pada titik di mana kesalahan terdeteksi?
  • Bisakah saya menggunakannya untuk melaporkan masalah dari fungsi yang dideklarasikan noexcept?

Di masa lalu, saya telah mendekati masalah pertama dengan menulis kode seperti ini

int
factorial(const int n)
{
  if (CHECK_ARGS)
    {
      if (n < 0)
        throw std::invalid_argument {"n < 0"};
    }
  int fac = 1;
  for (int i = 2; i <= n; ++i)
    fac *= i;
  return fac;
}

mana CHECK_ARGSyang #defined ke waktu kompilasi konstan sehingga compiler dapat sepenuhnya menghilangkan semua argumen kode pengecekan di dioptimalkan membangun. (Saya tidak mengatakan bahwa mengkompilasi cek adalah hal yang baik secara umum, tetapi saya percaya bahwa pengguna harus memiliki opsi untuk mengkompilasinya.)

Saya masih suka tentang solusi ini bahwa kode pemeriksaan argumen terlihat jelas dikelompokkan bersama ke dalam if. Namun, masalah kedua dan ketiga tidak terpecahkan dengan ini. Oleh karena itu, saya sekarang lebih condong ke arah menggunakan assertmakro untuk memeriksa argumen.

Standar pengkodean Boost setuju dengan ini:

Bagaimana dengan Kesalahan Programmer?

Sebagai pengembang, jika saya melanggar prasyarat perpustakaan yang saya gunakan, saya tidak ingin tumpukan dibuka. Apa yang saya inginkan adalah dump inti atau yang setara - cara untuk memeriksa status program pada titik yang tepat di mana masalah terdeteksi. Itu biasanya berarti assert()atau sesuatu seperti itu.

Ada ceramah yang sangat menarik yang diberikan oleh John Lakos di CppCon'14 berjudul Defensive Programming Done Right ( bagian 1 , bagian 2 ). Pada bagian pertama ceramahnya, ia membahas teori kontrak dan perilaku yang tidak terdefinisi. Pada bagian kedua, dia menyajikan apa yang saya anggap proposal yang sangat bagus untuk pemeriksaan argumen sistematis. Pada dasarnya, ia mengusulkan makro pernyataan yang memungkinkan pengguna untuk memilih berapa banyak anggaran (dalam hal penggunaan CPU) dia bersedia menyumbang ke perpustakaan untuk memeriksa argumen dan membuat perpustakaan bijaksana menggunakan anggaran itu. Sebagai tambahan, pengguna juga dapat menginstal fungsi penanganan kesalahan global yang akan dipanggil jika kontrak yang rusak terdeteksi.

Mengenai aspek bahwa suatu fungsi bersifat pribadi, saya tidak berpikir bahwa ini berarti kita seharusnya tidak pernah memeriksa argumennya. Kita mungkin lebih mempercayai kode kita sendiri untuk tidak melanggar kontrak fungsi internal tetapi kita juga tahu bahwa kita juga tidak sempurna. Mengecek argumen dalam fungsi internal sama membantu dalam mendeteksi bug kita sendiri seperti halnya dalam fungsi publik untuk mendeteksi bug dalam kode klien.

5gon12eder
sumber
1

Pertimbangkan struktur berikut:

  1. Logika internal: Fungsi ini diasumsikan dipanggil dengan parameter yang benar dan karenanya menggunakan penegasan untuk memverifikasi prasyarat, persyaratan akhir, dan invarian untuk memeriksa logika bagian dalam Anda.

  2. Antarmuka pengguna wrapper: Fungsi ini membungkus fungsi internal dan menggunakan InvalidArgumentExceptions untuk menangani nilai-nilai yang salah dan untuk memberitahu pengguna untuk memperbaiki input nya: Assert(x).hasLength(4);, Assume(y).isAlphanumeric();, Assert(z).isZipCode();, Assume(mailAdress).matchesRegex(regex_MailAdress);, Reject(x).ifEmpty();, dll

  3. Batch interface wrapper: Fungsi ini membungkus fungsi internal dan menggunakan logging, tanda validitas dan statistik untuk menangani nilai yang salah tanpa mengganggu beberapa tugas yang sudah berjalan lama. Penandaan tersebut dapat digunakan kemudian oleh seseorang yang memeriksa dan membersihkan database hasil.

  4. Pembungkus antarmuka baris perintah: fungsi ini membungkus fungsi internal dan meminta lagi input terakhir.

Anda harus menggunakan keduanya - menegaskan dan pengecualian - dalam metode yang berbeda untuk tugas yang berbeda. Anda harus memisahkan logika internal dari pengecekan parameter. Bandingkan dengan pemisahan Model, View, Controller.

Ralf K.
sumber
0

Ada cara yang lebih baik untuk menghindari cek referensi nol: gunakan kontrak kode atau kerangka kerja AOP untuk melakukan pemeriksaan untuk Anda. Google "c # code contract" atau "postsharp".

Codism
sumber
Saya kira pertanyaan saya akan meluas ke kontrak kode juga. Apakah ada kebutuhan untuk memeriksa prasyarat metode dalam metode pribadi (mis. Untuk pemeriksaan di masa depan, atau menghentikan Anda dari menembak diri sendiri di kaki)?
Mr Moose