Haruskah suatu metode memaafkan dengan argumen yang diteruskan? [Tutup]


21

Misalkan kita memiliki metode foo(String bar)yang hanya beroperasi pada string yang memenuhi kriteria tertentu; misalnya, harus huruf kecil, tidak boleh kosong atau hanya memiliki spasi putih, dan harus cocok dengan polanya [a-z0-9-_./@]+. Dokumentasi untuk metode ini menyatakan kriteria ini.

Haruskah metode tersebut menolak setiap dan semua penyimpangan dari kriteria ini, atau haruskah metode ini lebih memaafkan tentang beberapa kriteria? Misalnya, jika metode awal adalah

public void foo(String bar) {
    if (bar == null) {
        throw new IllegalArgumentException("bar must not be null");
    }
    if (!bar.matches(BAR_PATTERN_STRING)) {
        throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
    }
    this.bar = bar;
}

Dan metode memaafkan kedua adalah

public void foo(String bar) {
    if (bar == null) {
        throw new IllegalArgumentException("bar must not be null");
    }
    if (!bar.matches(BAR_PATTERN_STRING)) {
        bar = bar.toLowerCase().trim().replaceAll(" ", "_");
        if (!bar.matches(BAR_PATTERN_STRING) {
            throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
        }
    }
    this.bar = bar;
}

Haruskah dokumentasi diubah untuk menyatakan bahwa itu akan diubah dan diatur ke nilai yang ditransformasikan jika memungkinkan, atau haruskah metode ini dibuat sesederhana mungkin dan menolak setiap dan semua penyimpangan? Dalam hal ini, bardapat diatur oleh pengguna aplikasi.

Kasus penggunaan utama untuk ini adalah pengguna yang mengakses objek dari repositori oleh pengenal string tertentu. Setiap objek dalam repositori harus memiliki string unik untuk mengidentifikasinya. Repositori ini dapat menyimpan objek dengan berbagai cara (sql server, json, xml, binary, dll) dan jadi saya mencoba mengidentifikasi penyebut umum terendah yang cocok dengan kebanyakan konvensi penamaan.


1
Ini mungkin sangat tergantung pada kasus penggunaan Anda. Salah satu bisa masuk akal, dan saya bahkan telah melihat kelas yang menyediakan kedua metode dan membuat pengguna memutuskan. Bisakah Anda menguraikan apa yang seharusnya dilakukan metode / kelas / bidang ini, sehingga kami dapat menawarkan beberapa saran nyata?
Ixrec

1
Apakah Anda tahu semua orang yang memanggil metode ini? Seperti pada, jika Anda mengubahnya, dapatkah Anda mengidentifikasi semua klien dengan andal? Jika demikian, saya akan pergi dengan permisif dan memaafkan sebagaimana pertimbangan kinerja memungkinkan. Saya mungkin juga menghapus dokumentasinya. Jika tidak, dan ini merupakan bagian dari API perpustakaan, saya akan memastikan kode menerapkan persis API yang diiklankan, karena jika tidak mengubah kode agar sesuai dengan dokumentasi di masa depan cenderung menghasilkan laporan bug.
Jon Chesterfield

7
Anda bisa berpendapat bahwa Separation of Concerns mengatakan bahwa jika perlu, Anda harus memiliki foofungsi ketat yang ketat dalam argumen apa yang diterimanya, dan memiliki fungsi pembantu kedua yang dapat mencoba untuk "membersihkan" argumen yang akan digunakan foo. Dengan cara ini, masing-masing metode memiliki lebih sedikit untuk dilakukan sendiri, dan mereka dapat dikelola dan diintegrasikan dengan lebih bersih. Jika menuruni rute itu, mungkin juga akan membantu untuk menjauh dari desain pengecualian-berat; Anda dapat menggunakan sesuatu seperti Optionalsebagai gantinya, dan kemudian memiliki fungsi yang mengkonsumsi foopengecualian melempar jika perlu.
gntskn

1
Ini seperti menanyakan "seseorang yang salah padaku, haruskah aku memaafkan mereka?" Jelas ada situasi di mana salah satu atau yang lain sesuai. Pemrograman mungkin tidak serumit hubungan manusia, tetapi sudah pasti cukup kompleks bahwa resep selimut seperti ini tidak akan berfungsi.
Kilian Foth

2
@ Boggin Saya juga akan mengarahkan Anda ke Prinsip Robustness Dipertimbangkan kembali . Kesulitan datang ketika Anda perlu memperluas implementasi dan implementasi memaafkan mengarah ke kasus yang ambigu dengan implementasi yang diperluas.

Jawaban:


47

Metode Anda harus melakukan apa yang dikatakannya.

Ini mencegah bug, baik dari penggunaan maupun dari pengelola yang mengubah perilaku nanti. Ini menghemat waktu karena pengelola tidak perlu menghabiskan banyak waktu untuk mencari tahu apa yang sedang terjadi.

Yang mengatakan, jika logika yang didefinisikan tidak ramah pengguna, mungkin harus ditingkatkan.


8
Ini kuncinya. Jika metode Anda melakukan persis seperti yang dikatakannya, pembuat kode yang menggunakan metode Anda akan mengganti kasus penggunaan khusus mereka. Jangan pernah melakukan sesuatu yang tidak berdokumen dengan metode ini hanya karena Anda pikir itu berguna. Jika Anda perlu mengubahnya, tulis wadah atau ubah dokumentasinya.
Nelson

Saya akan menambahkan komentar @ Nelson bahwa metode ini tidak boleh dirancang dalam ruang hampa. Jika coders mengatakan mereka akan menggunakannya tetapi akan memberikan kompensasi dan kompensasi mereka memiliki nilai tujuan umum, pertimbangkan untuk menjadikan mereka bagian dari kelas. (Misalnya, miliki foodan fooForUncleanStringmetode di mana yang terakhir melakukan koreksi sebelum meneruskannya ke yang sebelumnya.)
Blrfl

20

Ada beberapa poin:

  1. Implementasi Anda harus melakukan apa yang dinyatakan dalam kontrak yang didokumentasikan, dan tidak boleh melakukan apa-apa lagi.
  2. Kesederhanaan penting, baik untuk kontrak maupun implementasi, meskipun lebih untuk yang pertama.
  3. Mencoba mengoreksi input yang salah menambah kerumitan, secara intuitif kontra tidak hanya untuk kontrak dan implementasi tetapi juga untuk digunakan.
  4. Kesalahan seharusnya hanya diketahui lebih awal jika hal itu meningkatkan kemampuan debuggabilitas dan tidak terlalu memengaruhi efisiensi.
    Ingat bahwa ada pernyataan debug untuk mendiagnosis kesalahan logika dalam mode debug, yang sebagian besar mengurangi masalah kinerja.
  5. Efisiensi, sejauh waktu dan uang yang tersedia memungkinkan tanpa terlalu banyak mengorbankan kesederhanaan, selalu menjadi tujuan.

Jika Anda menerapkan antarmuka pengguna, pesan kesalahan yang ramah (termasuk saran dan bantuan lainnya) adalah bagian dari desain yang baik.
Tetapi ingat bahwa API adalah untuk pemrogram, bukan pengguna akhir.


Eksperimen kehidupan nyata dalam menjadi kabur dan permisif dengan input adalah HTML.
Yang mengakibatkan semua orang melakukannya sedikit berbeda, dan spek, sekarang didokumentasikan, menjadi buku besar yang penuh dengan kasus-kasus khusus.
Lihat hukum Postel (" Jadilah konservatif dalam apa yang Anda lakukan, menjadi liberal dalam apa yang Anda terima dari orang lain. ") Dan kritik yang menyentuh itu ( Atau yang jauh lebih baik MichaelT membuat saya sadar ).


Sepotong kritis lain oleh penulis sendmail: The Robustness Principle Reconsidered

15

Perilaku metode harus jelas, intuitif, dapat diprediksi, dan sederhana. Secara umum, kita harus sangat ragu untuk melakukan pemrosesan ekstra pada input pemanggil. Dugaan seperti itu tentang apa yang dimaksudkan si penelepon selalu memiliki banyak kasus tepi yang menghasilkan perilaku yang tidak diinginkan. Pertimbangkan operasi sesederhana bergabung dengan jalur file. Banyak (atau bahkan mungkin sebagian besar) fungsi file path yang bergabung akan secara diam-diam membuang semua path sebelumnya jika salah satu path yang bergabung tampaknya telah di-root! Misalnya, /abc/xyzbergabung dengan /evilakan menghasilkan adil /evil. Ini hampir tidak pernah saya maksudkan ketika saya bergabung dengan jalur file, tetapi karena tidak ada antarmuka yang tidak berperilaku seperti ini, saya terpaksa memiliki bug atau menulis kode tambahan yang mencakup kasus-kasus ini.

Yang mengatakan, ada kesempatan langka ketika masuk akal untuk metode untuk menjadi "pemaaf," tetapi itu harus selalu berada dalam kekuatan penelepon untuk memutuskan kapan dan apakah langkah-langkah pemrosesan ini berlaku untuk situasi mereka. Jadi, ketika Anda telah mengidentifikasi langkah preprocessing umum yang ingin Anda terapkan pada argumen dalam berbagai situasi, Anda harus mengekspos antarmuka untuk:

  • Fungsionalitas mentah, tanpa preprocessing.
  • Langkah preprocessing dengan sendirinya .
  • Kombinasi fungsi mentah dan preprocessing.

Yang terakhir adalah opsional; Anda hanya harus menyediakannya jika banyak panggilan akan menggunakannya.

Mengekspos fungsi mentah memberi pemanggil kemampuan untuk menggunakannya tanpa langkah preprocessing ketika mereka perlu. Mengekspos langkah preprosesor dengan sendirinya memungkinkan pemanggil untuk menggunakannya untuk situasi di mana mereka bahkan tidak memanggil fungsi atau ketika mereka ingin preprocess beberapa input sebelum memanggil fungsi (seperti ketika mereka ingin meneruskannya ke fungsi lain terlebih dahulu). Menyediakan kombinasi memungkinkan penelepon untuk memanggil keduanya tanpa kerumitan, yang berguna terutama jika sebagian besar penelepon akan menggunakannya dengan cara ini.


2
+1 untuk dapat diprediksi. Dan +1 lainnya (saya harap) sederhana. Saya lebih suka Anda membantu saya mengenali dan memperbaiki kesalahan saya daripada menyembunyikannya.
John M Gant

4

Seperti yang dikatakan orang lain, membuat string yang cocok "memaafkan" berarti memperkenalkan kompleksitas tambahan. Itu berarti lebih banyak pekerjaan dalam mengimplementasikan pencocokan. Anda sekarang memiliki lebih banyak kasus uji, misalnya. Anda harus melakukan pekerjaan tambahan untuk memastikan tidak ada nama yang sederajat secara semantik di namespace. Lebih banyak kompleksitas juga berarti ada lebih banyak kesalahan di masa depan. Mekanisme yang lebih sederhana, seperti sepeda, membutuhkan perawatan yang lebih sedikit daripada yang lebih kompleks, seperti mobil.

Jadi apakah pencocokan string yang ringan sepadan dengan semua biaya tambahan itu? Itu tergantung pada use case, seperti yang telah dicatat orang lain. Jika string adalah semacam input eksternal yang tidak dapat Anda kendalikan, dan ada keuntungan pasti untuk pencocokan ringan, mungkin layak dilakukan. Mungkin input berasal dari pengguna akhir yang mungkin tidak terlalu berhati-hati tentang karakter ruang dan huruf besar, dan Anda memiliki insentif yang kuat untuk membuat produk Anda lebih mudah digunakan.

Di sisi lain, jika input berasal dari, katakanlah, file properti yang dikumpulkan oleh orang-orang teknis, yang harus memahami itu "Fred Mertz" != "FredMertz", saya akan lebih cenderung untuk membuat pencocokan lebih ketat dan menghemat biaya pengembangan.

Saya pikir dalam hal apapun ada nilai dalam memangkas dan mengabaikan ruang terkemuka dan tertinggal, meskipun - saya telah melihat terlalu banyak waktu terbuang untuk men-debug masalah-masalah semacam itu.


3

Anda menyebutkan beberapa konteks dari mana pertanyaan ini datang.

Mengingat bahwa, saya akan memiliki metode melakukan hanya satu hal, itu menegaskan persyaratan pada string, biarkan dijalankan berdasarkan itu - saya tidak akan mencoba untuk mengubahnya di sini. Tetap sederhana dan tetap jelas; mendokumentasikannya , dan berusaha untuk menjaga dokumentasi dan kode tetap sinkron satu sama lain.

Jika Anda ingin mengubah data yang berasal dari basis data pengguna dengan cara yang lebih memaafkan, masukkan fungsi itu ke dalam metode transformasi yang terpisah dan dokumentasikan fungsi yang diikatnya .

Pada titik tertentu persyaratan fungsi perlu diukur, didokumentasikan dengan jelas dan eksekusi harus dilanjutkan. "Pengampunan", pada poinnya, sedikit bisu, ini adalah keputusan desain dan saya berpendapat untuk fungsi yang tidak mengubah argumennya. Memiliki fungsi bermutasi input menyembunyikan beberapa validasi yang akan diperlukan klien. Memiliki fungsi yang melakukan mutasi membantu klien menyelesaikannya dengan benar.

The penekanan besar di sini adalah kejelasan dan dokumen apa kode tidak .


-1
  1. Anda dapat memberi nama metode sesuai dengan tindakan seperti doSomething (), takeBackUp ().
  2. Agar mudah perawatannya, Anda dapat mempertahankan kontrak dan validasi bersama pada berbagai prosedur. Sebut mereka sesuai kasus penggunaan.
  3. Pemrograman defensif: prosedur Anda menangani berbagai input termasuk (hal-hal minimum yang harus menggunakan kasus tetap harus dibahas)
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.