Kami memiliki kode ini yang, ketika disederhanakan, terlihat seperti ini:
public class Room
{
public Client Client { get; set; }
public long ClientId
{
get
{
return Client == null ? 0 : Client.Id;
}
}
}
public class Client
{
public long Id { get; set; }
}
Sekarang kami memiliki tiga sudut pandang.
1) Ini adalah kode yang baik karena Client
properti harus selalu disetel (yaitu bukan nol) sehingga Client == null
tidak akan pernah terjadi dan nilai Id 0
menunjukkan ID palsu pula (ini adalah pendapat penulis kode ;-))
2) Anda tidak dapat bergantung pada penelepon untuk mengetahui bahwa itu 0
adalah nilai yang salah untuk Id
dan ketika Client
properti harus selalu disetel Anda harus memasukkan exception
nilai get
ketika Client
properti tersebut bernilai nol.
3) Ketika Client
properti harus selalu disetel, Anda hanya kembali Client.Id
dan membiarkan kode tersebut memberikan NullRef
pengecualian ketika Client
properti tersebut bernilai nol.
Manakah dari ini yang paling benar? Atau apakah ada kemungkinan keempat?
sumber
Jawaban:
Baunya seperti Anda harus membatasi jumlah negara bagian
Room
kelas Anda .Fakta bahwa Anda bertanya tentang apa yang harus dilakukan ketika
Client
adalah nol adalah petunjuk bahwaRoom
ruang keadaan terlalu besar.Sederhananya, saya tidak akan membiarkan
Client
properti dariRoom
instance apa pun menjadi nol. Itu berarti kode di dalamnyaRoom
dapat dengan aman menganggapClient
tidak pernah nol.Jika karena alasan tertentu di masa depan
Client
menjadinull
menahan keinginan untuk mendukung negara itu. Melakukan hal itu akan menambah kompleksitas perawatan Anda.Sebagai gantinya, biarkan kode gagal dan gagal cepat. Bagaimanapun, ini bukan keadaan yang didukung. Jika aplikasi masuk ke keadaan ini, Anda sudah melewati garis tidak dapat kembali. Satu-satunya hal yang masuk akal untuk dilakukan adalah menutup aplikasi.
Ini mungkin terjadi (secara alami) sebagai hasil dari pengecualian referensi nol yang tidak ditangani.
sumber
null
nilainya denganNullObject
(atau lebih tepatnyaNullClient
) yang kemudian akan tetap bekerja dengan kode Anda yang ada dan memungkinkan Anda untuk menentukan perilaku klien yang tidak ada jika perlu. Pertanyaannya adalah apakah case "no client" adalah bagian dari logika bisnis atau tidak.Hanya beberapa pertimbangan:
a) Mengapa ada pengambil khusus untuk ClientId ketika sudah ada pengambil publik untuk instance Klien itu sendiri? Saya tidak melihat mengapa informasi bahwa ClientId
long
harus diukir dengan batu di tanda tangan Room.b) Mengenai pendapat kedua Anda bisa memperkenalkan sebuah konstanta
Invalid_Client_Id
.c) Mengenai pendapat satu dan tiga (dan menjadi poin utama saya): Kamar harus selalu memiliki klien? Mungkin hanya semantik tetapi ini kedengarannya tidak benar. Mungkin akan lebih tepat untuk memiliki kelas yang benar-benar terpisah untuk Kamar dan Klien dan kelas lain yang mengikat mereka bersama. Mungkin Pengangkatan, Pemesanan, Hunian? (Ini tergantung pada apa yang sebenarnya Anda lakukan.) Dan pada kelas itu Anda dapat menegakkan batasan "harus memiliki kamar" dan "harus memiliki klien".
sumber
Saya tidak setuju dengan ketiga pendapat tersebut. Jika
Client
tidak pernah bisanull
, maka jangan membuatnya menjadi mungkinnull
!Client
dalam konstruktorArgumentNullException
di konstruktor.Jadi kode Anda akan menjadi seperti:
Ini tidak terkait dengan kode Anda, tetapi Anda mungkin akan lebih baik menggunakan versi abadi
Room
dengan membuattheClient
readonly
, dan kemudian jika klien berubah, membuat ruang baru. Ini akan meningkatkan keamanan utas kode Anda selain keamanan nol dari aspek lain dari jawaban saya. Lihat diskusi ini tentang bisa berubah vs tidak berubahsumber
ArgumentNullException
?NullReferenceException
, itu dilemparkan oleh .Net VM "ketika ada upaya untuk referensi referensi objek nol" . Anda harus melemparArgumentNullException
, yang dilemparkan "ketika referensi nol (Tidak ada dalam Visual Basic) diteruskan ke metode yang tidak menerimanya sebagai argumen yang valid."readonly
). Saya baru saja mengedit tetapi saya merasa komentar akan lebih menjelaskan mengapa (untuk pembaca masa depan). Jika Anda belum memberikan jawaban ini, saya akan memberikan solusi yang sama persis. Kekekalan juga merupakan ide yang bagus, tetapi itu tidak selalu semudah yang diharapkan.Opsi kedua dan ketiga harus dihindari - rajin dan rajin giat memukul si penelepon dengan pengecualian mereka tidak memiliki kendali atas.
Anda harus memutuskan apakah Klien dapat menjadi nol. Jika demikian, Anda harus menyediakan cara bagi pemanggil untuk memeriksa apakah itu nol sebelum mengaksesnya (misalnya, bool properti ClientIsNull bool).
Jika Anda memutuskan bahwa Klien tidak akan pernah menjadi nol, maka jadikan parameter yang diperlukan untuk konstruktor dan berikan pengecualian di sana jika nol dilewatkan.
Akhirnya, opsi pertama juga berisi kode bau. Anda harus membiarkan Klien berurusan dengan properti ID-nya sendiri. Sepertinya berlebihan untuk membuat kode rajin dan giat dalam kelas wadah yang hanya memanggil rajin dan rajin pada kelas yang ada. Hanya mengekspos Klien sebagai properti (jika tidak, Anda akan akhirnya menduplikasi semua yang sudah ditawarkan oleh Klien ).
Jika Klien dapat menjadi nol, maka Anda setidaknya akan memberikan tanggung jawab kepada penelepon:
sumber
Jika properti Klien nol adalah keadaan yang didukung, pertimbangkan untuk menggunakan NullObject .
Tetapi kemungkinan besar ini adalah keadaan yang luar biasa, jadi Anda harus membuatnya tidak mungkin (atau tidak terlalu nyaman) untuk berakhir dengan nol
Client
:Namun jika tidak didukung, jangan buang waktu untuk solusi setengah matang. Pada kasus ini:
Anda telah 'memecahkan' NullReferenceException di sini tetapi dengan biaya yang besar! Ini akan memaksa semua penelepon
Room.ClientId
untuk memeriksa 0:Bug aneh dapat berganti dengan devs lain (atau diri Anda sendiri!) Lupa untuk memeriksa nilai kembali "ErrorCode" di beberapa titik waktu.
Mainkan dengan aman dan gagal dengan cepat. Biarkan NullReferenceException dilempar dan "anggun" tangkap di tempat yang lebih tinggi di tumpukan panggilan alih-alih memungkinkan penelepon mengacaukan segalanya ...
Pada catatan yang berbeda, jika Anda begitu tertarik untuk mengekspos
Client
dan jugaClientId
memikirkan TellDontAsk . Jika Anda bertanya terlalu banyak dari objek alih-alih memberi tahu mereka apa yang ingin Anda capai, Anda dapat mengakhiri menyatukan semuanya, membuat perubahan lebih sulit nantinya.sumber
NotSupportedException
disalahgunakan, Anda harus menggunakan sebagaiArgumentNullException
gantinya.Pada c # 6.0, yang sekarang dirilis, Anda harus melakukan ini,
Mengangkat properti
Client
keRoom
merupakan pelanggaran nyata enkapsulasi dan prinsip KERING.Jika Anda perlu mengakses
Id
aClient
dari yangRoom
dapat Anda lakukan,Perhatikan penggunaan Operator Bersyarat Null ,
clientId
akan menjadilong?
, jikaClient
adanull
,clientId
akannull
, jika tidakclientId
akan memiliki nilaiClient.Id
.sumber
clientid
adalah nullable lama itu?var clientId = room.Client.Id
keduanya akan aman danlong
.