Sebagai contoh, misalkan saya memiliki kelas Member
,, yang memiliki lastChangePasswordTime:
class Member{
.
.
.
constructor(){
this.lastChangePasswordTime=null,
}
}
yang terakhirChangePasswordTime dapat berarti tidak ada, karena beberapa anggota mungkin tidak pernah mengubah kata sandi mereka.
Tetapi menurut Jika nol itu jahat, apa yang harus digunakan ketika suatu nilai bisa tidak ada? dan https://softwareengineering.stackexchange.com/a/12836/248528 , saya tidak boleh menggunakan null untuk mewakili nilai yang tidak ada. Jadi saya mencoba menambahkan bendera Boolean:
class Member{
.
.
.
constructor(){
this.isPasswordChanged=false,
this.lastChangePasswordTime=null,
}
}
Tapi saya pikir itu cukup usang karena:
Ketika isPasswordChanged salah, lastChangePasswordTime harus menjadi nol, dan memeriksa lastChangePasswordTime == null hampir identik dengan memeriksa isPasswordChanged salah, jadi saya lebih suka memeriksa lastChangePasswordTime == null secara langsung
Saat mengubah logika di sini, saya mungkin lupa memperbarui kedua bidang.
Catatan: ketika pengguna mengubah kata sandi, saya akan mencatat waktu seperti ini:
this.lastChangePasswordTime=Date.now();
Apakah bidang Boolean tambahan lebih baik daripada referensi nol di sini?
std::optional
atauOption
. Dalam bahasa lain, Anda mungkin harus membangun mekanisme yang tepat sendiri, atau Anda mungkin benar-benar menggunakannull
atau sesuatu yang serupa karena lebih idiomatik.lastChangePasswordTime
mungkin pointer tidak diinisialisasi di sana, dan membandingkannya dengan apa pun akan menjadi perilaku yang tidak terdefinisi. Bukan alasan yang sangat kuat untuk tidak menginisialisasi pointer keNULL
/nullptr
sebagai gantinya, terutama tidak dalam C ++ modern (di mana Anda tidak akan menggunakan pointer sama sekali), tetapi siapa yang tahu? Contoh lain adalah bahasa tanpa pointer, atau dengan dukungan buruk untuk pointer, mungkin. (FORTRAN 77 muncul di benak ...)Jawaban:
Saya tidak mengerti mengapa, jika Anda tidak memiliki nilai yang berarti,
null
sebaiknya tidak digunakan jika Anda sengaja dan berhati-hati.Jika tujuan Anda adalah mengelilingi nilai
isPasswordChanged
nol untuk mencegah referensi secara tidak sengaja, saya sarankan membuat nilai sebagai fungsi atau properti yang mengembalikan hasil pemeriksaan nol, misalnya:Menurut pendapat saya, melakukannya dengan cara ini:
isPasswordChanged
nilai yang Anda sebutkan.Cara Anda mempertahankan data (mungkin dalam database) akan bertanggung jawab untuk memastikan bahwa nol tetap dipertahankan.
sumber
isPasswordChanged
sama seperti Anda lupa memeriksa apakah itu nol. Saya melihat tidak ada yang didapat di sini.nulls
tidak jahat. Menggunakannya tanpa berpikir adalah. Ini adalah kasus di mananull
jawaban yang tepat benar - tidak ada tanggal.Perhatikan bahwa solusi Anda menciptakan lebih banyak masalah. Apa arti dari tanggal yang ditetapkan untuk sesuatu, tetapi
isPasswordChanged
itu salah? Anda baru saja membuat kasus informasi yang saling bertentangan yang perlu Anda tangkap dan perlakukan secara khusus, sementaranull
nilai memiliki makna yang jelas, tidak ambigu, dan tidak dapat bertentangan dengan informasi lainnya.Jadi tidak, solusi Anda tidak lebih baik. Mengizinkan
null
nilai adalah pendekatan yang tepat di sini. Orang yang mengklaim itunull
selalu jahat tidak peduli konteksnya tidak mengerti mengapanull
ada.sumber
null
ada karena Tony Hoare membuat kesalahan miliaran dolar pada tahun 1965. Orang-orang yang mengklaim bahwanull
"jahat" terlalu menyederhanakan topik, tentu saja, tetapi itu adalah hal yang baik bahwa bahasa modern atau gaya pemrograman bergerak menjauh darinull
, menggantikan dengan jenis opsi khusus.DateTime
bidang adalah penunjuk. Seluruh konsepnull
hanya masuk akal untuk petunjuk!Tergantung pada bahasa pemrograman Anda, mungkin ada alternatif yang baik, seperti
optional
tipe data. Dalam C ++ (17), ini akan menjadi std :: opsional . Entah itu tidak ada atau memiliki nilai sewenang-wenang dari tipe data yang mendasarinya.sumber
Optional
di java juga; arti dari nolOptional
terserah padamu ...Optional<Date> lastPasswordChangeTime = null;
, tetapi saya tidak akan menyerah hanya karena itu. Sebaliknya, saya akan menanamkan aturan tim yang dipegang teguh, bahwa tidak ada nilai opsional yang diizinkan untuk ditetapkannull
. Opsional membeli Anda terlalu banyak fitur bagus untuk menyerah begitu saja. Anda dapat dengan mudah menetapkan nilai default (orElse
atau dengan evaluasi malas:)orElseGet
, melempar kesalahan (orElseThrow
), mengubah nilai-nilai dengan cara aman nol (map
,flatmap
), dll.isPresent
hampir selalu merupakan bau kode, dalam pengalaman saya, dan pertanda yang cukup dapat diandalkan bahwa para pengembang yang menggunakan opsi-opsi ini "tidak ada gunanya". Memang, pada dasarnya tidak ada manfaatnyaref == null
, tetapi itu juga bukan sesuatu yang harus sering Anda gunakan. Bahkan jika tim tidak stabil, alat analisis statis dapat menetapkan hukum, seperti linter atau FindBugs , yang dapat menangkap sebagian besar kasus.null
karena bahasa tersebut 100% kompatibel dengan Java, tetapi tidak ada yang menggunakannya, danOption[T]
semuanya ada di sana, dengan dukungan pemrograman fungsional yang sangat baik sepertimap
,,flatMap
pencocokan pola, dan sebagainya, yang berjalan jauh, jauh melampaui== null
cek. Anda benar bahwa dalam teori, jenis opsi terdengar sedikit lebih dari sekadar penunjuk yang dimuliakan, tetapi kenyataan membuktikan bahwa argumen itu salah.Menggunakan null dengan benar
Ada berbagai cara penggunaan
null
. Cara yang paling umum dan benar secara semantik adalah menggunakannya ketika Anda mungkin atau mungkin tidak memiliki nilai tunggal . Dalam hal ini nilai sama dengannull
atau adalah sesuatu yang bermakna seperti catatan dari database atau sesuatu.Dalam situasi ini, Anda lebih sering menggunakannya seperti ini (dalam pseudo-code):
Masalah
Dan itu memiliki masalah yang sangat besar. Masalahnya adalah pada saat Anda meminta
doSomethingUseful
nilainya mungkin belum diperiksanull
! Jika tidak maka program kemungkinan akan macet. Dan pengguna bahkan mungkin tidak melihat pesan kesalahan yang baik, dibiarkan dengan sesuatu seperti "kesalahan mengerikan: nilai yang diinginkan tetapi mendapat nol!" (setelah pembaruan: meskipun mungkin ada kesalahan yang kurang informatif sepertiSegmentation fault. Core dumped.
, atau lebih buruk lagi, tidak ada kesalahan dan manipulasi salah pada null dalam beberapa kasus)Lupa menulis cek
null
dan menanganinull
situasi adalah bug yang sangat umum . Inilah sebabnya mengapa Tony Hoare yang menemukannull
mengatakan pada konferensi perangkat lunak bernama QCon London pada 2009 bahwa ia membuat kesalahan miliaran dolar pada tahun 1965: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Kesalahan-Tony-HoareMenghindari masalah
Beberapa teknologi dan bahasa membuat pengecekan
null
tidak mungkin dilupakan dengan cara yang berbeda, mengurangi jumlah bug.Misalnya Haskell memiliki
Maybe
monad bukan nol. MisalkanDatabaseRecord
adalah tipe yang ditentukan pengguna. Dalam Haskell nilai tipeMaybe DatabaseRecord
bisa samaJust <somevalue>
atau mungkin sama denganNothing
. Anda kemudian dapat menggunakannya dengan cara yang berbeda tetapi tidak peduli bagaimana Anda menggunakannya, Anda tidak dapat menerapkan beberapa operasiNothing
tanpa menyadarinya.Misalnya fungsi ini disebut
zeroAsDefault
pengembalianx
untukJust x
dan0
untukNothing
:Christian Hackl mengatakan C ++ 17 dan Scala memiliki cara mereka sendiri. Jadi, Anda mungkin ingin mencoba mencari tahu apakah bahasa Anda memiliki sesuatu seperti itu dan menggunakannya.
Nulls masih digunakan secara luas
Jika Anda tidak memiliki yang lebih baik maka menggunakan
null
itu tidak masalah. Tetap waspada untuk itu. Ketik deklarasi dalam fungsi akan sedikit membantu Anda.Mungkin itu kedengarannya tidak terlalu progresif tetapi Anda harus memeriksa apakah rekan kerja Anda ingin menggunakan
null
atau sesuatu yang lain. Mereka mungkin konservatif dan mungkin tidak ingin menggunakan struktur data baru karena beberapa alasan. Misalnya mendukung versi bahasa yang lebih lama. Hal-hal seperti itu harus dinyatakan dalam standar pengkodean proyek dan didiskusikan dengan baik dengan tim.Atas proposal Anda
Anda menyarankan menggunakan bidang boolean yang terpisah. Tetapi Anda harus memeriksanya dan mungkin masih lupa untuk memeriksanya. Jadi tidak ada yang menang di sini. Jika Anda bahkan dapat melupakan sesuatu yang lain, seperti memperbarui kedua nilai setiap kali, maka itu lebih buruk lagi. Jika masalah lupa untuk memeriksa
null
tidak terpecahkan maka tidak ada gunanya. Menghindarinull
itu sulit dan Anda seharusnya tidak melakukannya sedemikian rupa sehingga membuatnya lebih buruk.Bagaimana tidak menggunakan null
Akhirnya ada cara umum untuk menggunakan secara
null
tidak benar. Salah satu cara adalah menggunakannya di tempat struktur data kosong seperti array dan string. Array kosong adalah array yang tepat seperti yang lainnya! Itu hampir selalu penting dan berguna untuk struktur data, yang dapat memuat banyak nilai, untuk dapat kosong, yaitu memiliki 0 panjang.Dari sudut pandang aljabar, string kosong untuk string sama seperti 0 untuk angka, yaitu identitas:
String kosong memungkinkan string secara umum menjadi monoid: https://en.wikipedia.org/wiki/Monoid Jika Anda tidak mendapatkannya, itu tidak penting bagi Anda.
Sekarang mari kita lihat mengapa penting untuk pemrograman dengan contoh ini:
Jika kita melewatkan array kosong di sini kode akan berfungsi dengan baik. Itu tidak akan melakukan apa-apa. Namun jika kita melewati di
null
sini maka kita kemungkinan akan mengalami crash dengan kesalahan seperti "tidak dapat melewati null, maaf". Kita bisa membungkusnyaif
tetapi itu kurang bersih dan lagi, Anda mungkin lupa memeriksanyaCara menangani null
Apa yang
doSomethingAboutIt()
harus dilakukan dan terutama apakah harus mengeluarkan pengecualian adalah masalah rumit lainnya. Singkatnya itu tergantung pada apakahnull
nilai input yang dapat diterima untuk tugas yang diberikan dan apa yang diharapkan sebagai respons. Pengecualian untuk acara yang tidak diharapkan. Saya tidak akan masuk lebih jauh ke topik itu. Jawaban ini sudah sangat lama.sumber
horrible error: wanted value but got null!
jauh lebih baik daripada yang lebih khasSegmentation fault. Core dumped.
...Error: the product you want to buy is out of stock
.null
tempatnull
yang tidak diizinkan adalah kesalahan pemrograman, yaitu bug dalam kode. Produk kehabisan stok adalah bagian dari logika bisnis biasa dan bukan bug. Anda tidak dapat, dan tidak boleh mencoba, menerjemahkan deteksi bug ke pesan normal di antarmuka pengguna. Menabrak segera dan tidak mengeksekusi lebih banyak kode adalah tindakan yang disukai, terutama jika itu adalah aplikasi penting (misalnya yang melakukan transaksi keuangan nyata).null
sepenuhnya, bahkan dari latar belakang.Selain semua jawaban yang sangat baik yang diberikan sebelumnya, saya akan menambahkan bahwa setiap kali Anda tergoda untuk membiarkan bidang menjadi nol, pikirkan baik-baik jika itu mungkin jenis daftar. Tipe nullable adalah daftar elemen 0 atau 1 yang ekivalen, dan seringkali ini dapat digeneralisasi ke daftar elemen N. Khususnya dalam hal ini, Anda mungkin ingin mempertimbangkan untuk
lastChangePasswordTime
menjadi daftarpasswordChangeTimes
.sumber
Tanyakan pada diri Anda sendiri: perilaku mana yang membutuhkan bidang lastChangePasswordTime?
Jika Anda memerlukan bidang itu untuk metode IsPasswordExpired () untuk menentukan apakah Anggota harus sering diminta untuk mengubah kata sandi mereka, saya akan mengatur bidang tersebut ke waktu Anggota awalnya dibuat. Implementasi IsPasswordExpired () adalah sama untuk anggota baru dan yang sudah ada.
Jika Anda memiliki persyaratan terpisah bahwa anggota yang baru dibuat harus memperbarui kata sandi mereka, saya akan menambahkan bidang boolean terpisah yang disebut kata sandiShouldBeChanged dan setel ke true saat membuat. Saya kemudian akan mengubah fungsionalitas metode IsPasswordExpired () untuk menyertakan cek untuk bidang itu (dan mengganti nama metode menjadi ShouldChangePassword).
Buat niat Anda eksplisit dalam kode.
sumber
Pertama, nulls evil adalah dogma dan seperti biasa dengan dogma itu bekerja paling baik sebagai pedoman dan bukan sebagai lulus / tidak lulus ujian.
Kedua, Anda dapat mendefinisikan kembali situasi Anda dengan cara yang masuk akal bahwa nilainya tidak akan pernah menjadi nol. InititialPasswordChanged adalah Boolean yang awalnya disetel ke false, PasswordSetTime adalah tanggal dan waktu ketika kata sandi saat ini disetel.
Perhatikan bahwa meskipun ini memerlukan sedikit biaya, kini Anda SELALU dapat menghitung berapa lama sejak kata sandi terakhir kali ditetapkan.
sumber
Keduanya 'aman / waras / benar' jika penelepon memeriksa sebelum digunakan. Masalahnya adalah apa yang terjadi jika penelepon tidak memeriksa. Mana yang lebih baik, beberapa rasa kesalahan nol atau menggunakan nilai yang tidak valid?
Tidak ada jawaban yang benar. Itu tergantung pada apa yang Anda khawatirkan.
Jika crash benar - benar buruk tetapi jawabannya tidak kritis atau memiliki nilai default yang diterima, maka mungkin menggunakan boolean sebagai flag lebih baik. Jika menggunakan jawaban yang salah adalah masalah yang lebih buruk daripada menabrak, maka menggunakan null lebih baik.
Untuk sebagian besar kasus 'tipikal', kegagalan cepat dan memaksa penelepon untuk memeriksa adalah cara tercepat untuk waras kode, dan karenanya saya pikir nol harus menjadi pilihan default. Saya tidak akan terlalu percaya pada penginjil "X adalah akar dari semua kejahatan"; mereka biasanya tidak mengantisipasi semua kasus penggunaan.
sumber