Pertimbangkan metode berikut:
public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{
}
Dan panggilan berikut:
var ids = ReturnEmployeeIds(true);
Untuk pengembang yang baru mengenal sistem, akan sangat sulit menebak apa yang true
terjadi. Hal pertama yang akan Anda lakukan adalah mengarahkan kursor ke nama metode atau pergi ke definisi (tidak ada yang merupakan tugas besar sedikit pun). Tetapi, demi keterbacaan, apakah masuk akal untuk menulis:
var ids = ReturnEmployeeIds(includeManagement: true);
Apakah ada tempat yang, secara formal, membahas apakah secara eksplisit menentukan parameter opsional atau tidak ketika Anda membutuhkannya?
Artikel berikut membahas konvensi pengkodean tertentu: https://msdn.microsoft.com/en-gb/library/ff926074.aspx
Sesuatu yang mirip dengan artikel di atas akan sangat bagus.
c#
coding-style
readability
parameters
JᴀʏMᴇᴇ
sumber
sumber
boolean
argumen metode, dan bagaimana?Jawaban:
Saya akan mengatakan bahwa di dunia C #, enum akan menjadi salah satu opsi terbaik di sini.
Dengan itu Anda akan dipaksa untuk menguraikan apa yang Anda lakukan dan setiap pengguna akan melihat apa yang terjadi. Itu juga aman untuk ekstensi di masa depan;
Jadi, menurut saya di sini:
enum> enum opsional> bool opsional
Sunting: Karena diskusi dengan LeopardSkinPillBoxHat di bawah ini mengenai
[Flags]
enum yang dalam kasus khusus ini mungkin cocok (karena kita secara khusus berbicara tentang memasukkan / mengecualikan hal-hal), saya malah mengusulkan menggunakanISet<WhatToInclude>
sebagai parameter.Ini adalah konsep yang lebih "modern" dengan beberapa keunggulan, sebagian besar berasal dari fakta bahwa itu cocok dalam koleksi LINQ, tetapi juga yang
[Flags]
memiliki maksimal 32 grup. Kelemahan utama menggunakanISet<WhatToInclude>
adalah seberapa buruk set didukung dalam sintaks C #:Beberapa di antaranya mungkin dimitigasi oleh fungsi pembantu, baik untuk menghasilkan set dan untuk membuat "set shortcut" seperti
sumber
enum
pendekatannya, tapi saya bukan penggemar berat pencampuranInclude
danExclude
samaenum
seperti itu lebih sulit untuk memahami apa yang terjadi. Jika Anda ingin ini benar-benar dapat diperluas, Anda dapat membuatnya[Flags]
enum
, membuat semuanyaIncludeXxx
, dan menyediakanIncludeAll
yang diatur untukInt32.MaxValue
. Kemudian Anda dapat memberikan 2ReturnEmployeeIds
kelebihan - yang mengembalikan semua karyawan, dan yang mengambilWhatToInclude
parameter filter yang bisa merupakan kombinasi dari enum flags.[Flags]
adalah peninggalan dan hanya harus digunakan untuk alasan warisan atau kinerja. Saya pikir aISet<WhatToInclude>
adalah konsep yang jauh lebih baik (sayangnya C # kekurangan gula sintaksis untuk membuatnya bagus untuk digunakan).Flags
pendekatan karena memungkinkan pemanggil untuk lulusWhatToInclude.Managers | WhatToInclude.Cleaners
dan bitwise atau mengekspresikan dengan tepat bagaimana pemanggil akan memprosesnya (yaitu manajer atau pembersih). Gula sintaksis intuitif :-)Dapat diperdebatkan jika ini adalah "gaya yang baik", tapi IMHO ini setidaknya bukan gaya yang buruk, dan berguna, selama baris kode tidak menjadi "terlalu panjang". Tanpa parameter bernama, itu juga gaya umum untuk memperkenalkan variabel yang menjelaskan:
Gaya itu mungkin lebih umum di antara programmer C # daripada varian parameter bernama, karena parameter bernama bukan bagian dari bahasa sebelum Versi 4.0. Efeknya pada keterbacaan hampir sama, hanya perlu satu baris kode tambahan.
Jadi rekomendasi saya: jika Anda berpikir menggunakan enum atau dua fungsi dengan nama yang berbeda adalah "berlebihan", atau Anda tidak ingin atau tidak dapat mengubah tanda tangan untuk alasan yang berbeda, silakan dan gunakan parameter bernama.
sumber
includeManagement
sebagai lokalconst
dan menghindari menggunakan memori tambahan sama sekali.Jika Anda menemukan kode seperti:
Anda bisa menjamin apa yang ada di dalamnya
{}
akan seperti:Dengan kata lain, boolean digunakan untuk memilih antara dua tindakan: metode ini memiliki dua tanggung jawab. Ini dijamin bau kode . Kita harus selalu berusaha untuk memastikan bahwa metode hanya melakukan satu hal; mereka hanya memiliki satu tanggung jawab. Karena itu, saya berpendapat bahwa kedua solusi Anda adalah pendekatan yang salah. Menggunakan parameter opsional adalah tanda bahwa metode melakukan lebih dari satu hal. Parameter Boolean juga merupakan tanda ini. Memiliki keduanya pasti harus mengatur bel alarm berbunyi. Apa yang harus Anda lakukan karena itu, adalah refactor dua set fungsi yang berbeda menjadi dua metode terpisah. Berikan nama metode yang menjelaskan apa yang mereka lakukan, misalnya:
Ini akan meningkatkan keterbacaan kode dan memastikan Anda lebih baik mengikuti prinsip-prinsip SOLID.
EDIT Seperti yang telah ditunjukkan dalam komentar, kasus di sini bahwa kedua metode ini kemungkinan akan memiliki fungsionalitas bersama, dan bahwa fungsionalitas bersama kemungkinan besar akan dibungkus dengan fungsi pribadi yang akan memerlukan parameter boolean untuk mengontrol beberapa aspek dari apa yang dilakukannya. .
Dalam hal ini, haruskah nama parameter diberikan, meskipun kompiler tidak memerlukannya? Saya menyarankan tidak ada jawaban sederhana untuk itu dan bahwa penilaian diperlukan berdasarkan kasus per kasus:
Apakah file sumber, dan metodenya, kecil dan mudah dipahami? Jika ya, maka parameter yang disebutkan mungkin berjumlah noise. Jika tidak, mungkin akan sangat membantu. Jadi terapkan aturan sesuai keadaan.
sumber
public List<Guid> ReturnEmployeeIds(bool includeManagement) { calculateSomethingHereForBothBranches; if (includeManagement) return something else return something else }
, jadi membaginya menjadi dua metode mungkin berarti kedua metode tersebut akan memerlukan hasil perhitungan pertama sebagai parameter.Ya benar. Kode dokumentasi diri adalah hal yang indah. Seperti yang telah ditunjukkan oleh jawaban lain, ada cara untuk menghapus ambiguitas panggilan
ReturnEmployeeIds
dengan menggunakan enum, nama metode yang berbeda, dll. Namun terkadang Anda tidak dapat benar-benar menghindari kasus ini tetapi Anda tidak ingin pergi dan menggunakan mereka di mana-mana (kecuali jika Anda suka verbositas visual basic).Misalnya, mungkin membantu mengklarifikasi satu panggilan tetapi belum tentu yang lain.
Argumen bernama dapat membantu di sini:
Tidak menambahkan banyak kejelasan tambahan (saya akan menebak bahwa ini adalah parsing enum):
Ini sebenarnya dapat mengurangi kejelasan (jika seseorang tidak memahami apa kapasitas dalam daftar):
Atau di tempat yang tidak penting karena Anda menggunakan nama variabel yang bagus:
Bukan AFAIK. Mari kita membahas kedua bagian: satu-satunya waktu Anda perlu secara eksplisit menggunakan parameter bernama adalah karena kompiler mengharuskan Anda untuk melakukannya (umumnya itu untuk menghapus ambiguitas pernyataan). Jadi selain itu Anda bisa menggunakannya kapan pun Anda mau. Artikel dari MS ini memiliki beberapa saran tentang kapan Anda mungkin ingin menggunakannya tetapi tidak terlalu definitif https://msdn.microsoft.com/en-us/library/dd264739.aspx .
Secara pribadi, saya menemukan saya paling sering menggunakannya ketika saya membuat atribut khusus atau mematikan metode baru di mana parameter saya dapat berubah atau ditata ulang (atribut b / c dapat dicantumkan dalam urutan apa pun). Selain itu saya hanya menggunakannya ketika saya memberikan inline nilai statis, jika tidak, jika saya melewati variabel saya mencoba menggunakan nama variabel yang baik.
Pada akhirnya itu tergantung pada preferensi pribadi. Tentu saja ada beberapa kasus penggunaan ketika Anda harus menggunakan parameter bernama tetapi setelah itu Anda perlu membuat panggilan penilaian. IMO - Gunakan di mana saja yang Anda percaya akan membantu mendokumentasikan kode Anda, mengurangi ambiguitas, atau memungkinkan Anda untuk melindungi tanda tangan metode.
sumber
Jika parameternya jelas dari nama (sepenuhnya memenuhi syarat) metode dan keberadaannya wajar untuk apa yang seharusnya dilakukan metode ini, Anda tidak boleh menggunakan sintaks parameter bernama. Misalnya, di
ReturnEmployeeName(57)
dalamnya sudah sangat jelas itu57
adalah ID karyawan, jadi berlebihan untuk membubuhi keterangan dengan sintaks parameter bernama.Dengan
ReturnEmployeeIds(true)
, seperti yang Anda katakan, kecuali jika Anda melihat deklarasi / dokumentasi fungsi tidak mungkin untuk mencari tahu apatrue
artinya - jadi Anda harus menggunakan sintaks parameter bernama.Sekarang, pertimbangkan kasus ketiga ini:
Sintaks parameter bernama tidak digunakan di sini, tetapi karena kita meneruskan sebuah variabel ke
ReturnEmployeeIds
, dan variabel itu memiliki nama, dan nama itu kebetulan berarti hal yang sama dengan parameter yang kita lewat itu (ini sering terjadi - tetapi tidak selalu!) - kita tidak perlu sintaks parameter bernama untuk memahami artinya dari kode.Jadi, aturannya sederhana - jika Anda dapat dengan mudah memahami hanya dari metode panggil apa arti parameter, jangan gunakan parameter yang disebutkan. Jika Anda tidak bisa - itu adalah kekurangan dalam keterbacaan kode, dan Anda mungkin ingin memperbaikinya (tidak dengan harga berapa pun, tentu saja, tetapi Anda biasanya ingin kode tersebut dapat dibaca). Perbaikan tidak harus dinamai parameter - Anda juga dapat menempatkan nilai dalam variabel terlebih dahulu, atau menggunakan metode yang disarankan dalam jawaban lain - selama hasil akhirnya membuatnya mudah untuk memahami apa yang dilakukan parameter.
sumber
ReturnEmployeeName(57)
membuatnya segera jelas itu57
adalah ID.GetEmployeeById(57)
di sisi lain ...ReturnEmployeeName
untuk menunjukkan kasus di mana bahkan tanpa secara eksplisit menyebutkan parameter dalam nama metode, cukup masuk akal untuk mengharapkan orang untuk mencari tahu apa itu. Bagaimanapun, perlu dicatat bahwa tidak seperti ituReturnEmployeeName
, Anda tidak dapat secara wajar mengganti namaReturnEmployeeIds
menjadi sesuatu yang menunjukkan makna di balik parameter.Ya, saya pikir itu gaya yang bagus.
Ini paling masuk akal untuk konstanta Boolean dan integer.
Jika Anda memasukkan variabel, nama variabel harus memperjelas artinya. Jika tidak, Anda harus memberi variabel nama yang lebih baik.
Jika Anda memasukkan string, artinya seringkali jelas. Seperti jika saya melihat panggilan ke GetFooData ("Oregon"), saya pikir seorang pembaca dapat menebak bahwa parameternya adalah nama negara.
Tidak semua string jelas, tentu saja. Jika saya melihat GetFooData ("M") maka kecuali saya tahu fungsinya, saya tidak tahu apa artinya "M". Jika itu semacam kode, seperti M = "karyawan tingkat manajemen", maka kita mungkin harus memiliki enum daripada literal, dan nama enum harus jelas.
Dan saya kira string bisa menyesatkan. Mungkin "Oregon" dalam contoh saya di atas merujuk, bukan ke negara bagian, tetapi ke produk atau klien atau apa pun.
Tapi GetFooData (true) atau GetFooData (42) ... itu bisa berarti apa saja. Parameter yang dinamai adalah cara yang luar biasa untuk membuatnya mendokumentasikan diri. Saya telah menggunakan mereka untuk tujuan itu pada sejumlah kesempatan.
sumber
Tidak ada yang super jelas alasan yang mengapa harus menggunakan sintaks jenis ini.
Secara umum, cobalah untuk menghindari memiliki argumen boolean yang di kejauhan mungkin terlihat sewenang-wenang.
includeManagement
akan (kemungkinan besar) mempengaruhi hasilnya. Tapi argumennya sepertinya membawa "sedikit berat".Penggunaan enum telah dibahas, tidak hanya akan terlihat seperti argumen "membawa lebih banyak berat", tetapi juga akan memberikan skalabilitas pada metode ini. Namun ini mungkin bukan solusi terbaik dalam semua kasus, karena
ReturnEmployeeIds
-metode Anda harus berskala bersamaWhatToInclude
-enum (lihat NiklasJ jawaban ). Ini mungkin memberi Anda sakit kepala nanti.Pertimbangkan ini: Jika Anda skala
WhatToInclude
-enum, tetapi bukan -metodeReturnEmployeeIds
. Mungkin kemudian melemparArgumentOutOfRangeException
(kasus terbaik) atau mengembalikan sesuatu yang sama sekali tidak diinginkan (null
atau kosongList<Guid>
). Yang dalam beberapa kasus mungkin membingungkan programmer, terutama jika AndaReturnEmployeeIds
berada di perpustakaan kelas, di mana kode sumber tidak tersedia.Orang akan berasumsi bahwa itu
WhatToInclude.Trainees
akan berhasil jikaWhatToInclude.All
dilihat, melihat bahwa peserta pelatihan adalah "bagian" dari semuanya.Ini tentu saja tergantung pada bagaimana
ReturnEmployeeIds
penerapannya.Dalam kasus di mana argumen boolean dapat diajukan, saya mencoba memecahnya menjadi dua metode (atau lebih, jika diperlukan), dalam kasus Anda; orang dapat mengekstrak
ReturnAllEmployeeIds
,ReturnManagementIds
danReturnRegularEmployeeIds
. Ini mencakup semua pangkalan dan sangat jelas bagi siapa pun yang mengimplementasikannya. Ini juga tidak akan memiliki masalah "penskalaan", yang disebutkan di atas.Karena hanya ada dua hasil untuk sesuatu yang memiliki argumen boolean. Menerapkan dua metode, membutuhkan sedikit usaha ekstra.
Kode kurang, jarang lebih baik.
Dengan kata, ada yang beberapa kasus di mana secara eksplisit menyatakan argumen meningkatkan keterbacaan. Pertimbangkan misalnya.
GetLatestNews(max: 10)
.GetLatestNews(10)
adalah masih cukup jelas, deklarasi eksplisitmax
akan membantu menjernihkan setiap kebingungan.Dan jika Anda benar-benar harus memiliki argumen boolean, penggunaan yang tidak dapat disimpulkan dengan hanya membaca
true
ataufalse
. Maka saya akan mengatakan itu:.. benar-benar lebih baik, dan lebih mudah dibaca daripada:
Karena dalam contoh kedua,
true
mungkin berarti apa pun . Tetapi saya akan mencoba untuk menghindari argumen ini.sumber