Pertanyaanmu.
Tapi, tapi - dari kenaifan saya, izinkan saya menjerit - kadang-kadang nilai BISA tidak ada!
Sepenuhnya dengan Anda di sana. Namun, ada beberapa peringatan yang akan saya bahas dalam sedetik. Tapi pertama-tama:
Null itu jahat dan harus dihindari sebisa mungkin.
Saya pikir ini adalah pernyataan yang bermaksud baik tetapi terlalu umum.
Null bukanlah kejahatan. Mereka bukan antipattern. Mereka bukan sesuatu yang harus dihindari bagaimanapun caranya. Namun, nol adalah cenderung rentan terhadap kesalahan (karena gagal memeriksa nol).
Tapi saya tidak mengerti bagaimana kegagalan untuk memeriksa null adalah bukti bahwa null secara inheren salah untuk digunakan.
Jika null jahat, maka indeks array dan pointer C ++ juga sama. Mereka tidak. Mereka mudah menembak diri sendiri dengan kaki, tetapi mereka tidak seharusnya dihindari dengan cara apa pun.
Untuk sisa jawaban ini, saya akan menyesuaikan niat "nulls are evil" ke yang lebih bernuansa " nulls harus ditangani secara bertanggung jawab ".
Solusi Anda.
Sementara saya setuju dengan pendapat Anda tentang penggunaan null sebagai aturan global; Saya tidak setuju dengan penggunaan nol Anda dalam kasus khusus ini.
Untuk meringkas umpan balik di bawah ini: Anda salah memahami tujuan pewarisan dan polimorfisme. Sementara argumen Anda untuk menggunakan null memiliki validitas, "bukti" selanjutnya tentang mengapa cara lain yang buruk dibangun di atas penyalahgunaan warisan, menjadikan poin Anda agak tidak relevan dengan masalah nol.
Sebagian besar pembaruan secara alami memiliki Player yang terkait dengannya - lagipula, perlu diketahui pemain mana yang melakukan perubahan ini. Namun, beberapa pembaruan tidak dapat memiliki Pemain yang terkait secara bermakna dengannya.
Jika pembaruan ini berbeda bermakna (yang mana), maka Anda memerlukan dua jenis pembaruan yang terpisah.
Pertimbangkan pesan, misalnya. Anda memiliki pesan kesalahan dan pesan obrolan IM. Tetapi sementara mereka mungkin berisi data yang sangat mirip (pesan string dan pengirim), mereka tidak berperilaku dengan cara yang sama. Mereka harus digunakan secara terpisah, sebagai kelas yang berbeda.
Apa yang Anda lakukan sekarang adalah membedakan secara efektif antara dua jenis pembaruan berdasarkan kekosongan properti Player. Itu sama dengan memutuskan antara pesan IM dan pesan kesalahan berdasarkan keberadaan kode kesalahan. Itu bekerja, tapi itu bukan pendekatan terbaik.
- Kode JS sekarang hanya akan menerima objek tanpa Player sebagai properti yang ditentukan, yang sama seperti jika itu nol karena kedua kasus memerlukan pemeriksaan apakah nilainya ada atau tidak;
Argumen Anda bergantung pada kesalahan polimorfisme. Jika Anda memiliki metode yang mengembalikan GameUpdate
objek, umumnya tidak perlu peduli untuk membedakan GameUpdate
, PlayerGameUpdate
atau NewlyDevelopedGameUpdate
.
Kelas dasar harus memiliki kontrak yang berfungsi penuh, yaitu propertinya didefinisikan pada kelas dasar dan bukan pada kelas turunan (yang dikatakan, nilai properti-properti dasar ini tentu saja dapat ditimpa dalam kelas turunan).
Ini membuat argumen Anda bisa diperdebatkan. Dalam praktik yang baik, Anda tidak boleh peduli bahwa GameUpdate
objek Anda memiliki atau tidak memiliki pemain. Jika Anda mencoba mengakses Player
properti a GameUpdate
, maka Anda melakukan sesuatu yang tidak masuk akal.
Bahasa yang diketik seperti C # bahkan tidak akan memungkinkan Anda untuk mencoba dan mengakses Player
properti GameUpdate
karena GameUpdate
tidak memiliki properti. Javascript jauh lebih longgar dalam pendekatannya (dan tidak memerlukan prakompilasi) sehingga Javascript tidak repot-repot mengoreksi Anda dan malah membuatnya meledak saat runtime.
- Jika bidang nullable lainnya ditambahkan ke kelas GameUpdate, saya harus dapat menggunakan banyak pewarisan untuk melanjutkan desing ini - tetapi MI itu jahat dalam dirinya sendiri (menurut programmer yang lebih berpengalaman) dan yang lebih penting, C # tidak memilikinya jadi saya tidak bisa menggunakannya.
Anda seharusnya tidak membuat kelas berdasarkan penambahan properti yang dapat dibatalkan. Anda harus membuat kelas berdasarkan jenis pembaruan game yang unik dan fungsional .
Bagaimana cara menghindari masalah dengan null secara umum?
Saya pikir itu relevan untuk menguraikan bagaimana Anda dapat secara bermakna mengekspresikan tidak adanya nilai. Ini adalah kumpulan solusi (atau pendekatan buruk) tanpa urutan tertentu.
1. Tetap gunakan null.
Ini adalah pendekatan termudah. Namun, Anda mendaftar sendiri untuk satu ton cek nol dan (ketika gagal melakukannya) pemecahan masalah pengecualian referensi nol.
2. Gunakan nilai tidak berarti yang bukan nol
Contoh sederhana di sini adalah indexOf()
:
"abcde".indexOf("b"); // 1
"abcde".indexOf("f"); // -1
Alih-alih null
, Anda dapatkan -1
. Pada tingkat teknis, ini adalah nilai integer yang valid. Namun, nilai negatif adalah jawaban yang tidak masuk akal karena indeks diharapkan bilangan bulat positif.
Secara logis, ini hanya dapat digunakan untuk kasus-kasus di mana tipe pengembalian memungkinkan nilai-nilai yang lebih mungkin daripada yang dapat dikembalikan secara bermakna (indexOf = bilangan bulat positif; int = bilangan bulat positif dan positif)
Untuk jenis referensi, Anda masih bisa menerapkan prinsip ini. Misalnya, jika Anda memiliki entitas dengan ID integer, Anda bisa mengembalikan objek dummy dengan ID yang disetel ke -1, sebagai kebalikan dari nol.
Anda hanya perlu mendefinisikan "dummy state" di mana Anda dapat mengukur apakah suatu objek benar-benar dapat digunakan atau tidak.
Perhatikan bahwa Anda tidak harus bergantung pada nilai string yang telah ditentukan jika Anda tidak mengontrol nilai string. Anda mungkin mempertimbangkan untuk menetapkan nama pengguna ke "null" ketika Anda ingin mengembalikan objek kosong, tetapi Anda akan mengalami masalah ketika Christopher Null mendaftar untuk layanan Anda .
3. Lempar pengecualian sebagai gantinya.
Tidak, tidak. Pengecualian tidak harus ditangani untuk aliran logis.
Yang sedang berkata, ada beberapa kasus di mana Anda tidak ingin menyembunyikan pengecualian (nullreference), tetapi menunjukkan pengecualian yang sesuai. Sebagai contoh:
var existingPerson = personRepository.GetById(123);
Ini bisa melempar PersonNotFoundException
(atau serupa) ketika tidak ada orang dengan ID 123.
Agak jelas, ini hanya dapat diterima untuk kasus-kasus di mana tidak menemukan item membuat mustahil untuk melakukan pemrosesan lebih lanjut. Jika tidak menemukan item mungkin dan aplikasi dapat dilanjutkan, maka Anda TIDAK PERNAH harus melemparkan pengecualian . Saya tidak bisa cukup menekankan hal ini.