Katakanlah saya memiliki fungsi IsAdmin
yang memeriksa apakah pengguna adalah admin. Misalkan saja pengecekan admin dilakukan dengan mencocokkan id pengguna, nama, dan kata sandi dengan semacam aturan (tidak penting).
Di kepala saya ada dua kemungkinan tanda tangan fungsi untuk ini:
public bool IsAdmin(User user);
public bool IsAdmin(int id, string name, string password);
Saya paling sering menggunakan jenis tanda tangan kedua, berpikir bahwa:
- Fungsi tanda tangan memberi pembaca lebih banyak info
- Logika yang terkandung di dalam fungsi tidak harus tahu tentang
User
kelas - Biasanya menghasilkan kode sedikit lebih sedikit di dalam fungsi
Namun saya terkadang mempertanyakan pendekatan ini, dan juga menyadari bahwa pada titik tertentu akan menjadi sulit. Jika misalnya fungsi akan memetakan antara sepuluh bidang objek yang berbeda ke dalam bool yang dihasilkan, saya jelas akan mengirim seluruh objek. Tetapi terlepas dari contoh nyata seperti itu saya tidak bisa melihat alasan untuk meneruskan objek yang sebenarnya.
Saya akan menghargai argumen apa pun untuk gaya apa pun, serta pengamatan umum apa pun yang Anda tawarkan.
Saya memprogram dalam gaya berorientasi objek dan fungsional, jadi pertanyaannya harus dilihat mengenai setiap dan semua idiom.
sumber
Jawaban:
Saya pribadi lebih suka metode pertama saja
IsAdmin(User user)
Ini jauh lebih mudah digunakan, dan jika kriteria Anda untuk IsAdmin berubah di kemudian hari (mungkin berdasarkan peran, atau isActive), Anda tidak perlu menulis ulang tanda tangan metode Anda di mana-mana.
Mungkin juga lebih aman karena Anda tidak mengiklankan properti apa yang menentukan apakah pengguna adalah seorang Admin atau tidak, atau membagikan properti kata sandi di mana-mana. Dan syrion membuat poin bagus bahwa apa yang terjadi ketika Anda
id
tidak cocok denganname
/password
?Panjang kode di dalam suatu fungsi seharusnya tidak terlalu penting memberikan metode melakukan tugasnya, dan saya lebih suka memiliki kode aplikasi yang lebih pendek dan lebih sederhana daripada kode metode pembantu.
sumber
Tanda tangan pertama lebih unggul karena memungkinkan enkapsulasi logika terkait pengguna dalam
User
objek itu sendiri. Tidaklah bermanfaat memiliki logika untuk pembuatan tuple "id, name, password" yang bertebaran tentang basis kode Anda. Lebih jauh lagi, itu memperumitisAdmin
fungsi: apa yang terjadi jika seseorang melewati sebuahid
yang tidak cocok denganname
, misalnya? Bagaimana jika Anda ingin memeriksa apakah Pengguna yang diberikan adalah administrator dari konteks di mana Anda seharusnya tidak mengetahui kata sandi mereka?Selanjutnya, sebagai catatan pada poin ketiga Anda mendukung gaya dengan argumen tambahan; mungkin menghasilkan "kode kurang di dalam fungsi," tetapi di mana logika itu pergi? Itu tidak bisa hilang begitu saja! Alih-alih, itu tersebar di setiap tempat di mana fungsi dipanggil. Untuk menyimpan lima baris kode di satu tempat, Anda telah membayar lima baris per penggunaan fungsi!
sumber
user.IsAdmin()
akan jauh lebih baik?IsAdmin
untuk bentuk atau fungsi tertentu, itu harus tidak terkait dengan penggunaYang pertama, tetapi tidak (hanya) karena alasan yang diberikan orang lain.
Ini jenis aman. Terutama karena Pengguna adalah tipe yang Anda tentukan sendiri, ada sedikit atau tidak ada kesempatan untuk mengubah atau mengubah argumen. Ini jelas beroperasi pada Pengguna dan bukan suatu fungsi generik yang menerima int dan dua string. Ini adalah bagian besar dari titik menggunakan bahasa tipe-aman seperti Java atau C # yang terlihat seperti kode Anda. Jika Anda bertanya tentang bahasa tertentu, Anda mungkin ingin menambahkan tag untuk bahasa itu ke pertanyaan Anda.
Di sini, setiap int dan dua string akan dilakukan. Apa yang mencegah Anda mengubah nama dan kata sandi? Yang kedua adalah lebih banyak pekerjaan untuk digunakan dan memungkinkan lebih banyak kesempatan untuk kesalahan.
Agaknya, kedua fungsi mengharuskan user.getId (), user.getName (), dan user.getPassword () dipanggil, baik di dalam fungsi (dalam kasus pertama) atau sebelum memanggil fungsi (dalam yang kedua), sehingga jumlah kopling adalah sama dengan cara yang sama. Sebenarnya, karena fungsi ini hanya valid pada Pengguna, di Java saya akan menghilangkan semua argumen dan menjadikan ini metode instan pada objek Pengguna:
Karena fungsi ini sudah tergabung erat dengan Pengguna, masuk akal untuk menjadikannya bagian dari apa yang dilakukan pengguna.
PS Saya yakin ini hanya sebuah contoh, tetapi sepertinya Anda menyimpan kata sandi. Anda seharusnya hanya menyimpan hash kata sandi yang aman secara kriptografis. Selama login, kata sandi yang diberikan harus di hash dengan cara yang sama dan dibandingkan dengan hash yang disimpan. Mengirim kata sandi di sekitar dalam teks biasa harus dihindari. Jika Anda melanggar aturan ini dan Ismin (int Str) adalah untuk mencatat nama pengguna, maka jika Anda mengubah nama dan kata sandi dalam kode Anda, kata sandi dapat dicatat sebagai gantinya yang menciptakan masalah keamanan (jangan menulis kata sandi untuk log ) dan argumen lain yang mendukung melewati objek alih-alih bagian komponennya (atau menggunakan metode kelas).
sumber
Jika bahasa pilihan Anda tidak lulus struktur berdasarkan nilai, maka
(User user)
varian tampaknya lebih baik. Bagaimana jika suatu hari Anda memutuskan untuk menghapus nama pengguna dan hanya menggunakan ID / kata sandi untuk mengidentifikasi pengguna?Juga, pendekatan ini pasti mengarah pada pemanggilan metode yang panjang dan berlebihan, atau ketidakkonsistenan. Apakah memberikan 3 argumen tidak apa-apa? Bagaimana dengan 5? Atau 6? Mengapa dipikir-pikir itu, jika melewati suatu objek murah dalam hal sumber daya (bahkan lebih murah daripada melewati 3 argumen, mungkin)?
Dan saya (semacam) tidak setuju bahwa pendekatan kedua memberi pembaca lebih banyak info - secara intuitif, pemanggilan metode bertanya "apakah pengguna memiliki hak admin", bukan "apakah kombinasi id / nama / kata sandi yang diberikan memiliki hak admin".
Jadi, kecuali melewati
User
objek akan menghasilkan menyalin struktur besar, pendekatan pertama lebih bersih dan lebih logis bagi saya.sumber
Saya mungkin akan memiliki keduanya. Yang pertama sebagai API eksternal, dengan beberapa harapan stabilitas waktu. Yang kedua sebagai implementasi pribadi yang disebut oleh API eksternal.
Jika suatu saat nanti saya harus mengubah aturan pengecekan, saya hanya akan menulis fungsi pribadi baru dengan tanda tangan baru yang disebut oleh yang eksternal.
Ini memiliki keuntungan dari kemudahan untuk mengubah implementasi batin. Juga sangat biasa bahwa pada suatu waktu Anda mungkin membutuhkan kedua fungsi yang tersedia pada waktu yang sama dan memanggil satu atau yang lain tergantung dari beberapa perubahan konteks eksternal (misalnya Anda mungkin telah hidup bersama Pengguna lama dan Pengguna baru dengan bidang yang berbeda).
Mengenai judul pertanyaan, dengan cara dalam kedua kasus Anda memberikan informasi minimum yang diperlukan. Pengguna tampaknya adalah Struktur Data Terkait Aplikasi terkecil yang berisi informasi. Tiga kolom id, kata sandi, dan nama tampaknya merupakan data implementasi terkecil yang benar-benar dibutuhkan, tetapi ini sebenarnya bukan objek tingkat Aplikasi.
Dengan kata lain Jika Anda berurusan dengan database, Pengguna akan menjadi catatan, sedangkan id, kata sandi dan login akan menjadi bidang dalam catatan itu.
sumber
Ada satu titik negatif untuk mengirim kelas (tipe referensi) ke metode, dan itu adalah, Kerentanan Mass-Assignment . Bayangkan bahwa alih-alih
IsAdmin(User user)
metode, saya punyaUpdateUser(User user)
metode.Jika
User
kelas memiliki properti Boolean yang dipanggilIsAdmin
, dan jika saya tidak memeriksanya selama eksekusi metode saya, maka saya rentan terhadap penugasan massal. GitHub diserang oleh tanda tangan ini pada tahun 2012.sumber
Saya akan menggunakan pendekatan ini:
Menggunakan tipe antarmuka sebagai parameter untuk fungsi ini memungkinkan Anda untuk meneruskan objek Pengguna, menentukan objek tiruan Pengguna untuk pengujian, dan memberi Anda lebih banyak fleksibilitas seputar penggunaan dan pemeliharaan fungsi ini.
Saya juga menambahkan kata kunci
this
untuk menjadikan fungsi metode ekstensi dari semua kelas IUser; dengan cara ini Anda dapat menulis kode dengan cara yang lebih ramah OO dan menggunakan fungsi ini dalam permintaan Linq. Simpler masih akan mendefinisikan fungsi ini di IUser dan Pengguna, tapi saya berasumsi ada beberapa alasan mengapa Anda memutuskan untuk meletakkan metode ini di luar kelas itu?Saya menggunakan antarmuka khusus
IID
alih-alihint
karena ini memungkinkan Anda untuk mendefinisikan kembali properti ID Anda; misalnya jika Anda merasa perlu untuk mengubah dariint
kelong
,Guid
atau sesuatu yang lain. Itu mungkin berlebihan, tetapi selalu baik untuk mencoba membuat hal-hal fleksibel sehingga Anda tidak terkunci dalam keputusan awal.NB: Objek IUser Anda dapat berada dalam ruang nama dan / atau perakitan yang berbeda dengan kelas Pengguna, sehingga Anda memiliki opsi untuk memisahkan kode Pengguna Anda dari kode IsAdmin Anda, dengan mereka hanya berbagi satu pustaka yang direferensikan. Apa yang Anda lakukan di sana adalah panggilan bagaimana Anda mencurigai barang-barang ini digunakan.
sumber
Penafian:
Untuk balasan saya, saya akan fokus pada masalah gaya dan melupakan apakah ID, login dan kata sandi biasa adalah seperangkat parameter yang baik untuk digunakan, dari sudut pandang keamanan. Anda harus dapat memperkirakan jawaban saya untuk set data dasar apa pun yang Anda gunakan untuk mencari tahu tentang hak istimewa pengguna ...
Saya juga anggap
user.isAdmin()
setaraIsAdmin(User user)
. Itu pilihan bagi Anda untuk membuat.Balasan:
Rekomendasi saya hanya untuk pengguna saja:
Atau gunakan keduanya, di sepanjang baris:
Alasan untuk metode publik:
Saat menulis metode publik, biasanya ide yang baik untuk memikirkan bagaimana mereka akan digunakan.
Untuk kasus khusus ini, alasan mengapa Anda biasanya memanggil metode ini adalah untuk mencari tahu apakah pengguna tertentu adalah administrator. Itu metode yang Anda butuhkan. Anda benar-benar tidak ingin setiap pemanggil harus memilih parameter yang tepat untuk dikirim (dan risiko kesalahan karena duplikasi kode).
Alasan untuk metode pribadi:
Metode pribadi yang hanya menerima set minimum parameter yang diperlukan terkadang bisa menjadi hal yang baik. Terkadang tidak terlalu banyak.
Salah satu hal baik tentang pemisahan metode ini adalah Anda dapat memanggil versi pribadi dari beberapa metode publik di kelas yang sama. Misalnya jika Anda ingin (untuk alasan apa pun) memiliki logika yang sedikit berbeda untuk
Localuser
danRemoteUser
(mungkin ada pengaturan untuk menghidupkan / mematikan admin jarak jauh?):Selain itu, jika karena alasan apa pun Anda perlu menjadikan metode pribadi ini publik ... Anda bisa. Ini semudah hanya mengubah
private
kepublic
. Kelihatannya ini bukan keuntungan besar untuk kasus ini, tetapi terkadang memang benar.Juga, ketika pengujian unit Anda akan dapat membuat lebih banyak tes atom. Biasanya sangat baik tidak harus membuat objek pengguna penuh untuk menjalankan tes pada metode ini. Dan jika Anda ingin jangkauan penuh, Anda dapat menguji kedua panggilan.
sumber
buruk karena alasan yang tercantum. Itu tidak berarti
secara otomatis baik. Khususnya jika User adalah obyek yang memiliki metode seperti:
getOrders()
,getFriends()
,getAdresses()
, ...Anda mungkin mempertimbangkan untuk refactoring domain dengan jenis UserCredentials yang hanya berisi id, nama, kata sandi, dan meneruskannya ke IsAdmin alih-alih semua data Pengguna yang tidak dibutuhkan.
sumber