Saya pernah mendengar beberapa suara mengatakan bahwa memeriksa nilai null yang dikembalikan dari metode adalah desain yang buruk. Saya ingin mendengar beberapa alasan untuk ini.
kodesemu:
variable x = object.method()
if (x is null) do something
Saya pernah mendengar beberapa suara mengatakan bahwa memeriksa nilai null yang dikembalikan dari metode adalah desain yang buruk. Saya ingin mendengar beberapa alasan untuk ini.
kodesemu:
variable x = object.method()
if (x is null) do something
Jawaban:
Alasan di balik tidak mengembalikan nol adalah bahwa Anda tidak perlu memeriksanya dan karenanya kode Anda tidak perlu mengikuti jalur yang berbeda berdasarkan nilai pengembalian. Anda mungkin ingin memeriksa Pola Objek Null yang memberikan informasi lebih lanjut tentang ini.
Misalnya, jika saya mendefinisikan metode di Jawa yang mengembalikan Koleksi, saya biasanya lebih suka mengembalikan koleksi kosong (yaitu Collections.emptyList()
) daripada nol karena itu berarti kode klien saya lebih bersih; misalnya
Collection<? extends Item> c = getItems(); // Will never return null.
for (Item item : c) { // Will not enter the loop if c is empty.
// Process item.
}
... yang lebih bersih dari:
Collection<? extends Item> c = getItems(); // Could potentially return null.
// Two possible code paths now so harder to test.
if (c != null) {
for (Item item : c) {
// Process item.
}
}
null
adalah "wadah", yang berisi nol atau satu pointer ke objek. (Yang pasti bagaimana itu secara eksplisit dimodelkan oleh Haskell Maybe
dalam kasus-kasus itu, dan semuanya lebih baik untuk melakukannya.)
Inilah alasannya.
Dalam Clean Code oleh Robert Martin ia menulis bahwa mengembalikan null adalah desain yang buruk ketika Anda dapat kembali, katakanlah, array kosong. Karena hasil yang diharapkan adalah array, mengapa tidak? Ini akan memungkinkan Anda untuk mengulangi hasil tanpa kondisi tambahan. Jika bilangan bulat, mungkin 0 akan cukup, jika hash, hash kosong. dll.
Premisnya adalah untuk tidak memaksa kode panggilan untuk segera menangani masalah. Kode panggilan mungkin tidak ingin peduli dengan mereka. Itu juga mengapa dalam banyak kasus pengecualian lebih baik daripada nol.
Penggunaan kembali null dengan baik:
Penggunaan buruk: Mencoba mengganti atau menyembunyikan situasi luar biasa seperti:
Dalam kasus tersebut, melemparkan pengecualian lebih memadai karena:
Namun, Pengecualian tidak boleh digunakan untuk menangani kondisi operasi program normal seperti:
boolean login(String,String)
tampaknya baik-baik sajaAuthenticationContext createAuthContext(String,String) throws AuthenticationException
Ya, mengembalikan NULL adalah desain yang mengerikan , di dunia berorientasi objek. Singkatnya, penggunaan NULL mengarah ke:
Periksa posting blog ini untuk penjelasan terperinci: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Lebih banyak di buku saya Elegant Objects , Bagian 4.1.
bool TryGetBlah(out blah)
atau FirstOrNull()
atau MatchOrFallback(T fallbackValue)
.
isEmpty()
) dan hanya jika itu benar maka panggil metode tersebut. Orang-orang berdebat dengan yang ke-2 mengatakan itu adalah kinerja yang lebih buruk - tetapi seperti yang dikatakan Unix Philosophy, "nilai waktu manusia dari waktu ke waktu mesin" (yaitu, kinerja yang lebih lambat secara sepele menghabiskan waktu lebih sedikit daripada pengembang kode debugging yang memberikan kesalahan palsu).
Siapa bilang desain ini jelek?
Memeriksa nol adalah praktik yang umum, bahkan dianjurkan, jika tidak Anda berisiko NullReferenceExceptions di mana-mana. Lebih baik menangani kesalahan dengan anggun daripada melemparkan pengecualian saat Anda tidak perlu melakukannya.
Berdasarkan apa yang Anda katakan sejauh ini, saya pikir tidak ada informasi yang cukup.
Mengembalikan nol dari metode CreateWidget () tampaknya buruk.
Mengembalikan nol dari metode FindFooInBar () tampaknya baik-baik saja.
Create...
mengembalikan instance baru, atau melempar ; Get...
mengembalikan contoh yang ada yang ada, atau melempar ; GetOrCreate...
mengembalikan instance yang ada, atau instance baru jika tidak ada, atau melempar ; Find...
mengembalikan contoh yang ada, jika ada, ataunull
. Untuk kueri koleksi - Get...
selalu mengembalikan koleksi, yang kosong jika tidak ada item yang cocok ditemukan.
Penemunya mengatakan ini adalah kesalahan satu miliar dolar!
Itu tergantung pada bahasa yang Anda gunakan. Jika Anda menggunakan bahasa seperti C # di mana cara idiomatis untuk menunjukkan kurangnya nilai adalah mengembalikan nol, maka mengembalikan nol adalah desain yang baik jika Anda tidak memiliki nilai. Atau, dalam bahasa seperti Haskell yang secara idiomatis menggunakan Mungkin monad untuk kasus ini, maka mengembalikan null akan menjadi desain yang buruk (jika itu mungkin).
null
dalam bahasa-bahasa seperti C # dan Java sering kelebihan beban dan diberi makna dalam domain. Jika Anda mencari null
kata kunci dalam spesifikasi bahasa, itu berarti "penunjuk yang tidak valid". Kemungkinan itu tidak ada artinya dalam domain masalah apa pun.
null
atau "tidak diinisialisasi"null
Jika Anda membaca semua jawaban, menjadi jelas jawaban untuk pertanyaan ini tergantung pada jenis metode.
Pertama, ketika sesuatu yang luar biasa terjadi (masalah IO dll), secara logis pengecualian dilemparkan. Ketika sesuatu yang luar biasa mungkin adalah sesuatu untuk topik yang berbeda ..
Setiap kali suatu metode diharapkan tidak memiliki hasil, ada dua kategori:
Sampai kita memiliki cara resmi untuk menyatakan bahwa suatu fungsi dapat atau tidak dapat mengembalikan nol, saya mencoba untuk memiliki konvensi penamaan untuk menyatakan demikian.
Seperti halnya Anda memiliki konvensi Try Something () untuk metode yang diharapkan gagal, saya sering memberi nama metode saya Safe Something () ketika metode mengembalikan hasil netral alih-alih nol.
Saya belum sepenuhnya setuju dengan namanya, tetapi tidak dapat menemukan yang lebih baik. Jadi saya menjalankannya untuk saat ini.
Saya memiliki kebaktian di daerah ini yang sangat membantu saya
Untuk kueri item tunggal:
Create...
mengembalikan contoh baru, atau melemparGet...
mengembalikan contoh yang ada yang ada, atau melemparGetOrCreate...
mengembalikan instance yang ada, atau instance baru jika tidak ada, atau melemparFind...
mengembalikan contoh yang ada, jika ada, ataunull
Untuk kueri koleksi:
Get...
selalu mengembalikan koleksi, yang kosong jika tidak ada barang yang cocok [1] ditemukan[1] diberi beberapa kriteria, eksplisit atau implisit, diberikan dalam nama fungsi atau sebagai parameter.
Get
ketika saya mengharapkannya ada di sana, sehingga jika tidak ada di sana, maka itu adalah kesalahan dan saya lempar - saya tidak perlu memeriksa nilai kembali. Saya menggunakan Find
jika saya benar-benar tidak tahu apakah itu ada atau tidak - maka saya perlu memeriksa nilai kembali.
Pengecualian adalah pengecualian untuk semua keadaan.
Jika fungsi Anda dimaksudkan untuk menemukan atribut yang terkait dengan objek yang diberikan, dan objek itu memang tidak memiliki atribut seperti itu, mungkin tepat untuk mengembalikan nol. Jika objek tidak ada, melempar pengecualian mungkin lebih tepat. Jika fungsi ini dimaksudkan untuk mengembalikan daftar atribut, dan tidak ada yang mengembalikan, mengembalikan daftar kosong masuk akal - Anda mengembalikan semua atribut nol.
Tidak apa-apa untuk mengembalikan nol jika hal itu bermakna dalam beberapa cara:
public String getEmployeeName(int id){ ..}
Dalam kasus seperti ini, sangat penting untuk mengembalikan nol jika id tidak sesuai dengan entitas yang ada, karena memungkinkan Anda untuk membedakan kasus di mana tidak ada kecocokan yang ditemukan dari kesalahan yang sah.
Orang mungkin berpikir ini buruk karena dapat disalahgunakan sebagai nilai pengembalian "khusus" yang menunjukkan kondisi kesalahan, yang tidak begitu baik, sedikit seperti mengembalikan kode kesalahan dari suatu fungsi tetapi membingungkan karena pengguna harus memeriksa pengembalian untuk null, alih-alih menangkap pengecualian yang sesuai, mis
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
Ini belum tentu desain yang buruk - karena dengan begitu banyak keputusan desain, itu tergantung.
Jika hasil dari metode ini adalah sesuatu yang tidak akan memiliki hasil yang baik dalam penggunaan normal, mengembalikan null baik-baik saja:
object x = GetObjectFromCache(); // return null if it's not in the cache
Jika harus selalu ada hasil non-nol, maka mungkin lebih baik untuk melemparkan pengecualian:
try {
Controller c = GetController(); // the controller object is central to
// the application. If we don't get one,
// we're fubar
// it's likely that it's OK to not have the try/catch since you won't
// be able to really handle the problem here
}
catch /* ... */ {
}
Optional<>
Untuk skenario tertentu, Anda ingin melihat kegagalan segera setelah itu terjadi.
Memeriksa NULL dan tidak menyatakan (untuk kesalahan programmer) atau melempar (untuk kesalahan pengguna atau pemanggil) dalam kasus kegagalan dapat berarti bahwa crash kemudian lebih sulit untuk dilacak, karena kasus aneh asli tidak ditemukan.
Selain itu, mengabaikan kesalahan dapat menyebabkan eksploitasi keamanan. Mungkin ke-nol-an berasal dari fakta bahwa buffer ditimpa atau sejenisnya. Sekarang, Anda tidak menabrak, yang berarti pengeksploitasi memiliki kesempatan untuk mengeksekusi dalam kode Anda.
Alternatif apa yang Anda lihat untuk mengembalikan nol?
Saya melihat dua kasus:
Dalam hal ini kita dapat: Mengembalikan Null atau melempar pengecualian (dicentang) (atau mungkin membuat item dan mengembalikannya)
Dalam hal ini kita bisa mengembalikan Null, mengembalikan daftar kosong atau membuang Pengecualian.
Saya percaya bahwa pengembalian nol mungkin kurang baik daripada alternatif karena itu mengharuskan klien untuk ingat untuk memeriksa nol, programmer lupa dan kode
x = find();
x.getField(); // bang null pointer exception
Di Jawa, melempar pengecualian yang diperiksa, RecordNotFoundException, memungkinkan kompiler untuk mengingatkan klien untuk menangani kasus.
Saya menemukan bahwa pencarian kembali daftar kosong bisa sangat nyaman - hanya mengisi tampilan dengan semua isi daftar, oh itu kosong, kode "hanya berfungsi".
Kadang-kadang, mengembalikan NULL adalah hal yang benar untuk dilakukan, tetapi khususnya ketika Anda berurusan dengan urutan dari berbagai jenis (array, daftar, string, apa-apa), mungkin lebih baik mengembalikan urutan panjang nol, karena mengarah ke kode yang lebih pendek dan mudah-mudahan lebih dimengerti, sambil tidak mengambil lebih banyak tulisan pada bagian pelaksana API.
Buat mereka memanggil metode lain setelah fakta untuk mencari tahu apakah panggilan sebelumnya adalah nol. ;-) Hei, itu cukup baik untuk JDBC
Ide dasar di balik utas ini adalah memprogram secara defensif. Artinya, kode terhadap yang tak terduga. Ada serangkaian balasan yang berbeda:
Adamski menyarankan untuk melihat Pola Objek Null, dengan jawaban yang dipilih untuk saran itu.
Michael Valenty juga menyarankan konvensi penamaan untuk memberi tahu pengembang apa yang mungkin diharapkan. ZeroConcept menyarankan penggunaan Exception yang tepat, jika itu alasan untuk NULL. Dan lain-lain.
Jika kita membuat "aturan" bahwa kita selalu ingin melakukan pemrograman defensif maka kita dapat melihat bahwa saran ini valid.
Tetapi kami memiliki 2 skenario pengembangan.
Kelas "ditulis" oleh pengembang: Penulis
Kelas "dikonsumsi" oleh pengembang (mungkin) lain: Pengembang
Terlepas dari apakah suatu kelas mengembalikan NULL untuk metode dengan nilai pengembalian atau tidak, Pengembang perlu menguji apakah objek tersebut valid.
Jika pengembang tidak dapat melakukan ini, maka Kelas / metode itu tidak deterministik. Artinya, jika "metode panggilan" untuk mendapatkan objek tidak melakukan apa yang "diiklankan" (mis. GetEmployee), ia telah melanggar kontrak.
Sebagai penulis kelas, saya selalu ingin menjadi baik dan defensif (dan deterministik) ketika membuat metode.
Jadi mengingat bahwa NULL atau NULL OBJECT (misalnya jika (karyawan sebagai NullEmployee.ISVALID)) perlu diperiksa dan yang mungkin perlu terjadi dengan kumpulan Karyawan, maka pendekatan objek nol adalah pendekatan yang lebih baik.
Tapi saya juga suka saran Michael Valenty tentang penamaan metode yang HARUS mengembalikan nol misalnya getEmployeeOrNull.
Seorang Penulis yang melempar pengecualian sedang menghapus pilihan bagi pengembang untuk menguji validitas objek, yang sangat buruk pada kumpulan objek, dan memaksa pengembang ke dalam penanganan pengecualian ketika membuat kode konsumsi mereka yang bercabang.
Sebagai pengembang yang mengkonsumsi kelas, saya berharap penulis memberi saya kemampuan untuk menghindari atau memprogram untuk situasi nol yang mungkin dihadapi kelas / metode mereka.
Jadi sebagai pengembang saya akan memprogram pertahanan terhadap NULL dari suatu metode. Jika penulis telah memberi saya kontrak yang selalu mengembalikan objek (NULL OBJECT selalu melakukannya) dan objek itu memiliki metode / properti untuk menguji validitas objek, maka saya akan menggunakan metode / properti itu untuk terus menggunakan objek , kalau tidak objek tidak valid dan saya tidak bisa menggunakannya.
Intinya adalah bahwa Penulis Kelas / Metode harus menyediakan mekanisme yang dapat digunakan Pengembang dalam pemrograman defensif mereka. Artinya, niat yang lebih jelas dari metode ini.
Pengembang harus selalu menggunakan pemrograman defensif untuk menguji validitas objek yang dikembalikan dari kelas / metode lain.
salam
GregJF
Pilihan lain untuk ini, adalah: mengembalikan beberapa nilai yang menunjukkan keberhasilan atau tidak (atau jenis kesalahan), tetapi jika Anda hanya perlu nilai boolean yang akan menunjukkan keberhasilan / gagal, mengembalikan nol untuk kegagalan, dan objek untuk sukses tidak akan kurang benar, lalu mengembalikan true / false dan mendapatkan objek melalui parameter.
Pendekatan lain akan menggunakan pengecualian untuk menunjukkan kegagalan, tetapi di sini - sebenarnya ada lebih banyak suara, yang mengatakan ini adalah praktik BAD (karena menggunakan pengecualian mungkin nyaman tetapi memiliki banyak kelemahan).
Jadi saya pribadi tidak melihat hal buruk dalam mengembalikan nol sebagai indikasi bahwa ada yang tidak beres, dan memeriksanya nanti (untuk mengetahui apakah Anda berhasil atau tidak). Juga, secara membabi buta berpikir bahwa metode Anda tidak akan mengembalikan NULL, dan kemudian mendasarkan kode Anda di atasnya, dapat menyebabkan kesalahan lain, kadang-kadang sulit ditemukan, (walaupun dalam kebanyakan kasus hanya akan merusak sistem Anda :), karena Anda akan merujuk ke 0x00000000 cepat atau lambat).
Fungsi nol yang tidak diinginkan dapat muncul selama pengembangan program yang kompleks, dan seperti kode mati, kejadian tersebut menunjukkan kelemahan serius dalam struktur program.
Fungsi atau metode nol sering digunakan sebagai perilaku default fungsi yang dapat direvisi atau metode yang dapat ditimpa dalam kerangka kerja objek.
Yah, itu pasti tergantung dari tujuan metode ... Kadang-kadang, pilihan yang lebih baik adalah dengan melemparkan pengecualian. Itu semua tergantung dari kasus ke kasus.
Jika kodenya seperti:
command = get_something_to_do()
if command: # if not Null
command.execute()
Jika Anda memiliki objek dummy yang mengeksekusi () metode tidak melakukan apa-apa, dan Anda mengembalikan itu alih-alih Null dalam kasus yang sesuai, Anda tidak perlu memeriksa untuk kasus Null dan sebaliknya bisa melakukan:
get_something_to_do().execute()
Jadi, di sini masalahnya bukan antara memeriksa NULL vs pengecualian, tetapi sebaliknya antara pemanggil harus menangani kasus khusus yang berbeda (dengan cara apa pun) atau tidak.
Untuk kasus penggunaan saya, saya perlu mengembalikan Peta dari metode dan kemudian mencari kunci tertentu. Tetapi jika saya mengembalikan Peta kosong, maka itu akan mengarah ke NullPointerException dan kemudian tidak akan jauh berbeda dengan mengembalikan null daripada Peta kosong. Tetapi dari Java8 dan seterusnya kita bisa menggunakan Opsional . Di atas adalah alasan mengapa konsep opsional diperkenalkan.
Hari,
Mengembalikan NULL ketika Anda tidak dapat membuat objek baru adalah praktik standar untuk banyak API.
Kenapa itu desain yang buruk saya tidak tahu.
Sunting: Ini berlaku untuk bahasa di mana Anda tidak memiliki pengecualian seperti C yang telah menjadi konvensi selama bertahun-tahun.
HTH
'Sangat bahagia,