Apakah cukup untuk metode dibedakan hanya dengan nama argumen (bukan tipe)?

36

Apakah cukup untuk metode dibedakan hanya dengan nama argumen (bukan tipe) atau lebih baik untuk nama lebih eksplisit?

Misalnya T Find<T>(int id)vs T FindById<T>(int id).

Apakah ada alasan yang baik untuk menamainya lebih eksplisit (yaitu menambahkan ById) vs mempertahankan nama argumen yang adil?

Salah satu alasan yang bisa saya pikirkan adalah ketika tanda tangan dari metode yang sama tetapi mereka memiliki makna yang berbeda.

FindByFirstName(string name) dan FindByLastName(string name)

Konrad
sumber
4
Jadi, ketika Anda membebani Find untuk dimasukkan T Find<T>(string name)atau (int size)bagaimana Anda berencana untuk menyelesaikan masalah yang tak terhindarkan?
UKMonkey
3
@UKMonkey masalah apa yang tidak terhindarkan?
Konrad
3
dalam kasus pertama: jika beberapa entri memiliki nama yang sama maka Anda harus mengubah tanda tangan fungsi; yang berarti orang-orang kemungkinan akan bingung dengan apa artinya kembali; Dalam kasus terakhir, argumennya sama - dan dengan demikian merupakan kelebihan muatan ilegal. Anda mulai memberi nama fungsi dengan "byX" atau membuat objek untuk argumen sehingga Anda bisa memiliki yang setara dengan kelebihan dengan argumen yang sama. Baik bekerja dengan baik untuk situasi yang berbeda.
UKMonkey
2
@UKMonkey Anda dapat memposting jawaban dengan beberapa contoh kode jika Anda mau
Konrad
3
Id mungkin harus menjadi IDobjek buram dan bukan hanya sebuah int. Dengan cara itu dapatkan waktu kompilasi memeriksa bahwa Anda tidak menggunakan id untuk int atau sebaliknya di beberapa bagian kode Anda. Dan dengan itu Anda dapat memiliki find(int value)dan find(ID id).
Bakuriu

Jawaban:

68

Tentu ada alasan bagus untuk menyebutkannya secara lebih eksplisit.

Ini terutama bukan definisi metode yang harus jelas, tetapi metode yang digunakan . Dan sementara findById(string id)dan find(string id)keduanya cukup jelas, ada perbedaan besar antara findById("BOB")dan find("BOB"). Dalam kasus sebelumnya Anda tahu bahwa literal acak, pada kenyataannya, adalah ID. Dalam kasus terakhir Anda tidak yakin - itu mungkin sebenarnya nama yang diberikan atau sesuatu yang lain sama sekali.

Kilian Foth
sumber
9
Kecuali dalam 99% kasus lain di mana Anda memiliki variabel atau properti nama adalah titik acuan: findById(id)vs find(id). Anda dapat melakukannya dengan cara ini.
Greg Burghardt
16
@GregBurghardt: Nilai tertentu tidak harus dinamai dengan cara yang sama untuk suatu metode dan pemanggilnya. Sebagai contoh, pertimbangkan double Divide(int numerator, int denominator)yang digunakan dalam metode: double murdersPerCapita = Divide(murderCount, citizenCount). Anda tidak dapat mengandalkan dua metode menggunakan nama variabel yang sama karena ada banyak kasus di mana itu tidak terjadi (atau ketika itu terjadi, itu penamaan yang buruk)
Flater
1
@Flater: Diberi kode dalam pertanyaan (menemukan barang-barang dari semacam penyimpanan persisten) Saya bayangkan Anda mungkin memanggil metode ini dengan "killedCitizenId" atau "citizenId" ... Saya benar-benar tidak berpikir argumen atau nama variabel adalah ambigu di sini. Dan jujur ​​saya bisa jalan baik ini. Ini sebenarnya pertanyaan yang sangat keras.
Greg Burghardt
4
@GregBurghardt: Anda tidak dapat menyaring aturan global dari satu contoh. Pertanyaan OP secara umum , tidak spesifik untuk contoh yang diberikan. Ya, ada beberapa kasus di mana menggunakan nama yang sama masuk akal, tetapi ada juga kasus di mana itu tidak terjadi. Oleh karena itu jawaban ini ,, yang terbaik adalah bertujuan untuk konsistensi bahkan jika itu tidak diperlukan dalam subset kasus penggunaan.
Flater
1
Parameter nama menyelesaikan kebingungan setelah kebingungan sudah ada , karena Anda perlu melihat definisi metode, sementara metode yang dinamai secara eksplisit sepenuhnya menghindari kebingungan dengan meminta nama hadir dalam pemanggilan metode. Juga, Anda tidak dapat memiliki dua metode dengan nama dan tipe argumen yang sama tetapi nama argumen yang berbeda, yang berarti Anda akan memerlukan nama eksplisit dalam kasus tertentu.
Darkhogg
36

Keuntungan FindById () .

  1. Masa depan-pemeriksaan : Jika Anda memulai dengan Find(int), dan kemudian harus menambahkan metode lain ( FindByName(string), FindByLegacyId(int), FindByCustomerId(int), FindByOrderId(int), dll), orang-orang seperti saya cenderung menghabiskan usia mencari FindById(int). Tidak benar-benar masalah jika Anda bisa dan akan berubah Find(int)menjadi FindById(int)begitu diperlukan - pemeriksaan di masa depan adalah tentang ini jika s.

  2. Lebih mudah dibaca . Findbaik-baik saja jika panggilannya seperti record = Find(customerId);Namun FindByIdsedikit lebih mudah untuk dibaca jika itu record = FindById(AFunction());.

  3. Konsistensi . Anda dapat secara konsisten menerapkan FindByX(int)/ FindByY(int)pola di mana-mana, tetapi Find(int X)/ Find(int Y)tidak mungkin karena mereka bertentangan.

Keuntungan Menemukan ()

  • CIUMAN. Findsederhana dan mudah, dan di samping operator[]itu salah satu dari 2 nama fungsi yang paling diharapkan dalam konteks ini. (Beberapa alternatif populer menjadi get, lookupatau fetch, tergantung pada konteks).
  • Sebagai aturan praktis, jika Anda memiliki nama fungsi yang merupakan kata tunggal terkenal yang secara akurat menjelaskan apa fungsi tersebut, gunakan itu. Bahkan jika ada nama multi-kata yang lebih panjang yang sedikit lebih baik dalam menggambarkan apa fungsinya. Contoh: Panjang vs NumberOfElements . Ada tradeoff, dan di mana menarik garis adalah subjek dari perdebatan yang sedang berlangsung.
  • Biasanya bagus untuk menghindari redundansi. Jika kita melihat FindById(int id), kita dapat dengan mudah menghapus redundansi dengan mengubahnya Find(int id), tetapi ada trade off - kita kehilangan kejelasan.

Atau Anda bisa mendapatkan keuntungan dari keduanya dengan menggunakan Id yang sangat diketik:

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

Implementasi Id<>: Sangat mengetik nilai ID di C #

Komentar di sini, serta di link di atas, mengangkat beberapa kekhawatiran tentang Id<Customer>bahwa saya ingin alamat:

  • Masalah 1: Ini penyalahgunaan obat generik. CustomerIddan OrderIDtipe yang berbeda ( customerId1 = customerId2;=> baik, customerId1 = orderId1;=> buruk), tetapi implementasinya hampir identik, sehingga kami dapat mengimplementasikannya baik dengan salin tempel atau dengan metaprogramming. Meskipun ada nilai dalam diskusi tentang mengekspos atau menyembunyikan generik, metaprogramming adalah tujuan generik.
  • Kekhawatiran 2: Itu tidak menghentikan kesalahan sederhana./Ini adalah solusi dalam mencari masalah Masalah utama yang dihapus dengan menggunakan Id sangat diketik adalah urutan argumen yang salah dalam panggilan ke DoSomething(int customerId, int orderId, int productId). Id yang diketik dengan kuat juga mencegah masalah lain, termasuk yang ditanyakan OP.
  • Kekhawatiran 3: Ini benar-benar hanya mengaburkan kode. Sulit untuk mengetahui apakah ada id yang ditahan int aVariable. Sangat mudah untuk mengetahui bahwa ID telah disimpan Id<Customer> aVariable, dan kami bahkan dapat mengatakan bahwa itu adalah ID pelanggan.
  • Kekhawatiran 4: Id ini bukan tipe yang kuat, hanya pembungkus. Stringhanyalah pembungkus di sekitar byte[]. Pembungkus, atau enkapsulasi, tidak bertentangan dengan pengetikan yang kuat.
  • Kekhawatiran 5: Sudah selesai direkayasa. Inilah versi minimalnya, walaupun saya merekomendasikan untuk menambahkan operator==dan operator!=juga, jika Anda tidak ingin bergantung secara eksklusif pada Equals:

.

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}
Peter
sumber
10

Cara lain untuk berpikir tentang ini adalah dengan menggunakan jenis keamanan bahasa.

Anda dapat menerapkan metode seperti:

Find(FirstName name);

Di mana FirstName adalah objek sederhana yang membungkus string yang berisi nama depan dan berarti tidak ada kebingungan tentang apa yang dilakukan metode ini, atau dalam argumen yang disebutnya.

3DPrintScanner
sumber
4
Tidak yakin apa jawaban Anda untuk pertanyaan OP. Apakah Anda merekomendasikan untuk menggunakan nama seperti "Temukan" dengan mengandalkan jenis argumen? Atau apakah Anda merekomendasikan untuk menggunakan nama-nama seperti itu hanya ketika ada jenis eksplisit untuk argumen (s), dan menggunakan nama yang lebih eksplisit seperti "FindById" di tempat lain? Atau apakah Anda merekomendasikan untuk memperkenalkan tipe eksplisit untuk membuat nama seperti "Temukan" lebih layak?
Doc Brown
2
@DocBrown Saya pikir yang terakhir, dan saya menyukainya. Ini sebenarnya mirip dengan jawaban Peter, iiuc. Dasar pemikiran seperti yang saya mengerti adalah dua kali lipat: (1) Jelas dari tipe argumen apa fungsinya; (2) Anda tidak dapat membuat kesalahan seperti. Yang string name = "Smith"; findById(name);dimungkinkan jika Anda menggunakan tipe umum non-deskriptif.
Peter - Reinstate Monica
5
Sementara secara umum saya adalah penggemar keamanan jenis waktu kompilasi, berhati-hatilah dengan jenis bungkus. Memperkenalkan kelas pembungkus demi keamanan tipe terkadang dapat menyulitkan API Anda jika dilakukan secara berlebihan. misalnya, seluruh "artis yang sebelumnya dikenal sebagai masalah" int yang dimiliki winapi; dalam jangka panjang saya akan mengatakan kebanyakan orang hanya melihat DWORD LPCSTRklon dll yang tak berujung dan berpikir "ini adalah int / string / dll." .
jrh
1
@ jrh Benar. Tes lakmus saya untuk memperkenalkan tipe "nominal" (hanya berbeda dalam nama) adalah ketika fungsi / metode umum tidak masuk akal dalam kasus penggunaan kami, misalnya ints sering dijumlahkan, dikalikan, dll. Yang tidak berarti untuk ID; jadi saya akan membuatnya IDberbeda int. Ini dapat menyederhanakan API, dengan mempersempit apa yang dapat kita lakukan dengan memberikan nilai (mis. Jika kita memiliki ID, itu hanya akan berfungsi find, bukan misalnya ageatau highscore). Sebaliknya, jika kita mendapati diri kita banyak berkonversi, atau menulis fungsi / metode yang sama (mis. find) Untuk banyak jenis, itu adalah tanda bahwa perbedaan kita terlalu baik
Warbo
1

Saya akan memilih deklarasi eksplisit seperti FindByID .... Perangkat lunak harus dibangun untuk Perubahan. Itu harus terbuka dan tertutup (SOLID). Jadi kelas terbuka untuk menambahkan metode menemukan yang mirip seperti katakanlah FindByName .. dll.

Tetapi FindByID ditutup dan implementasinya diuji unit.

Saya tidak akan menyarankan metode dengan predikat, itu bagus di tingkat generik. Bagaimana jika berdasarkan bidang (ByID) Anda memiliki metodologi yang berbeda lengkap.

BrightFuture1029
sumber
0

Saya terkejut tidak ada yang menyarankan untuk menggunakan predikat seperti berikut:

User Find(Predicate<User> predicate)

Dengan pendekatan ini tidak hanya Anda mengurangi permukaan API Anda, tetapi juga memberikan lebih banyak kontrol kepada pengguna yang menggunakannya.

Jika itu tidak cukup, Anda selalu dapat mengembangkannya sesuai kebutuhan Anda.

Aybe
sumber
1
Masalahnya adalah itu kurang efisien karena fakta bahwa ia tidak dapat mengambil keuntungan dari hal-hal seperti indeks.
Solomon Ucko