Yöntemlerden dönen bir boş değeri kontrol etmenin kötü tasarım olduğunu söyleyen bazı sesler duydum. Bunun için bazı nedenler duymak istiyorum.
pseudocode:
variable x = object.method()
if (x is null) do something
Yöntemlerden dönen bir boş değeri kontrol etmenin kötü tasarım olduğunu söyleyen bazı sesler duydum. Bunun için bazı nedenler duymak istiyorum.
pseudocode:
variable x = object.method()
if (x is null) do something
Yanıtlar:
Boş değer döndürmemenin ardındaki mantık, onu kontrol etmeniz gerekmemesi ve dolayısıyla kodunuzun dönüş değerine göre farklı bir yol izlemesine gerek olmamasıdır . Bununla ilgili daha fazla bilgi sağlayan Boş Nesne Kalıbı'na göz atmak isteyebilirsiniz .
Örneğin, Java'da bir Koleksiyon döndüren bir yöntem tanımlayacak olsaydım, genellikle Collections.emptyList()
istemci kodumun daha temiz olduğu anlamına geldiğinden boş bir koleksiyon (yani ) döndürmeyi tercih ederdim ; Örneğin
Collection<? extends Item> c = getItems(); // Will never return null.
for (Item item : c) { // Will not enter the loop if c is empty.
// Process item.
}
... şundan daha temizdir:
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
nesnelere sıfır veya bir işaretçi içeren bir "kapsayıcıdır". (Bu gibi Maybe
durumlarda Haskell'in açıkça modellediği kesinlikle bu ve bunu yapmak için daha iyi.)
Nedeni burada.
In Robert Martin Temiz Kanunu o null adlı dönen kötü yerine dönebilir tasarımı, diyelim ki, boş bir dizi yazıyor. Beklenen sonuç bir dizi olduğuna göre, neden olmasın? Herhangi bir ekstra koşul olmadan sonucu yinelemenizi sağlar. Eğer bir tamsayı ise, belki 0, bir karma ise boş bir karma ise yeterli olacaktır. vb.
Buradaki önerme, arama kodunu sorunları hemen ele almaya zorlamamaktır. Arama kodu onlarla ilgilenmek istemeyebilir. Bu yüzden çoğu durumda istisnalar sıfırdan daha iyidir.
Boş değer döndürmenin iyi kullanımları:
Kötü kullanımlar: Aşağıdakiler gibi istisnai durumları değiştirmeye veya gizlemeye çalışma:
Bu gibi durumlarda bir istisna yapmak daha uygundur çünkü:
Ancak, istisnalar aşağıdaki gibi normal program çalıştırma koşullarını işlemek için kullanılmamalıdır:
boolean login(String,String)
AuthenticationContext createAuthContext(String,String) throws AuthenticationException
Evet, NULL döndürmek , nesne yönelimli dünyada korkunç bir tasarımdır . Özetle, NULL kullanımı şunlara yol açar:
Ayrıntılı bir açıklama için bu blog gönderisine bakın: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Daha fazlası kitabımda Elegant Objects , Kısım 4.1.
bool TryGetBlah(out blah)
veya FirstOrNull()
veya MatchOrFallback(T fallbackValue)
.
isEmpty()
) Bir kontrol yöntemi çağırın ve yalnızca doğru ise yöntemi çağırın. İnsanlar, ikincisine karşı tartışıyorlar, performansın daha düşük olduğunu söylüyor - ancak Unix Philosophy'nin dediği gibi, "makine zamanına göre insan zamanına değer verin" (yani, önemsiz ölçüde daha yavaş performans, sahte hatalar veren kod hata ayıklama geliştiricilere göre daha az zaman harcar).
Bunun kötü bir tasarım olduğunu kim söylüyor?
Boş değerleri kontrol etmek yaygın bir uygulamadır, hatta teşvik edilir, aksi takdirde her yerde NullReferenceExceptions riskiyle karşılaşırsınız. İhtiyacınız olmadığında istisnalar atmaktansa hatayı incelikle ele almak daha iyidir.
Şimdiye kadar söylediklerinize dayanarak, yeterli bilgi olmadığını düşünüyorum.
CreateWidget () yönteminden boş değer döndürmek kötü görünüyor.
FindFooInBar () yönteminden boş değer döndürmek iyi görünüyor.
Create...
yeni bir örnek döndürür veya atar ; Get...
beklenen mevcut bir örneği döndürür veya atar ; GetOrCreate...
mevcut bir örneği veya yoksa yeni bir örneği döndürür veya atar ; Find...
varsa, mevcut bir örneği döndürür veyanull
. Koleksiyon sorguları için - Get...
her zaman eşleşen öğe bulunamazsa boş olan bir koleksiyon döndürür.
Mucidi , bunun milyar dolarlık bir hata olduğunu söylüyor !
Kullandığınız dile bağlıdır. Bir değerin eksikliğini belirtmenin deyimsel yolunun null döndürmek olduğu C # gibi bir dildeyseniz, değeriniz yoksa boş döndürmek iyi bir tasarımdır. Alternatif olarak, bu durum için deyimsel olarak Belki monadını kullanan Haskell gibi dillerde, boş döndürmek kötü bir tasarım olurdu (hatta mümkün olsaydı).
null
C # ve Java gibi dillerdeki tanımlarının genellikle aşırı yüklendiğini ve etki alanında bir miktar anlam verildiğini görüyorum . Eğer yukarı bakacak olursak null
dil özellikleri anahtar kelime, sadece "geçersiz işaretçi" anlamına gelir. Bu muhtemelen herhangi bir sorun alanında hiçbir şey ifade etmiyor.
null
yoksa "başlatılmamış" mı olduğunu bilmiyorsunuznull
Tüm cevapları okursanız, bu sorunun cevabı yöntemin türüne bağlıdır.
İlk olarak, istisnai bir şey olduğunda (IOproblem vb.), Mantıksal olarak istisnalar atılır. Tam olarak bir şey olağanüstü olduğunda, muhtemelen farklı bir konu için bir şeydir ..
Bir yöntemin herhangi bir sonuca sahip olmaması beklendiğinde, iki kategori vardır:
Bir işlevin boş dönebileceğini veya dönmeyeceğini belirtmenin resmi bir yolunu bulana kadar, bunu belirtmek için bir adlandırma kuralına sahip olmaya çalışıyorum. Başarısız olması beklenen yöntemler
için Try Something () kuralınız olduğu gibi, genellikle yöntemlerimi Güvenli Bir Şey () olarak adlandırırım. , yöntem null yerine nötr bir sonuç döndürdüğünde olarak adlandırırım.
Henüz isim konusunda tam olarak uygun değilim, ancak daha iyi bir şey bulamadım. Bu yüzden şimdilik bununla koşuyorum.
Bu alanda bana iyi hizmet eden bir kongrem var
Tek öğe sorguları için:
Create...
yeni bir örnek döndürür veya fırlatırGet...
beklenen mevcut bir örneği döndürür veya atarGetOrCreate...
mevcut bir örneği veya yoksa yeni bir örneği döndürür veya atarFind...
varsa, mevcut bir örneği döndürür veyanull
Koleksiyon sorguları için:
Get...
her zaman eşleşen [1] öğe bulunmazsa boş olan bir koleksiyon döndürür[1] işlev adında veya parametrelerde verilen açık veya örtük bazı kriterler verildi.
Get
ne zaman bekliyoruz orada olmak orada değil eğer öyleyse, o zaman bunun bir hata olduğuna ve ben atmak olduğunu, - ben dönüş değeri denetlemek için gerek kalmaz. Find
Orada olup olmadığını gerçekten bilmiyorsam kullanırım - o zaman dönüş değerini kontrol etmem gerekir.
İstisnalar istisna içindir durumlar içindir.
İşlevinizin belirli bir nesneyle ilişkili bir özniteliği bulması amaçlanıyorsa ve bu nesnenin böyle bir özniteliği yoksa, boş döndürmek uygun olabilir. Nesne mevcut değilse, bir istisna atmak daha uygun olabilir. İşlevin bir öznitelik listesi döndürmesi amaçlanıyorsa ve döndürülecek hiçbir şey yoksa, boş bir liste döndürmek mantıklıdır - tüm sıfır özniteliklerini döndürüyorsunuz.
Bunu yapmak bir şekilde anlamlıysa, boş döndürmek sorun değil:
public String getEmployeeName(int id){ ..}
Böyle bir durumda, kimlik mevcut bir varlığa karşılık gelmiyorsa boş döndürmek anlamlıdır, çünkü hiçbir eşleşme bulunmayan durumu meşru bir hatadan ayırt etmenize olanak tanır.
İnsanlar bunun kötü olduğunu düşünebilirler çünkü bir hata durumunu gösteren "özel" bir dönüş değeri olarak kötüye kullanılabilir, bu çok iyi olmayan bir işlevden hata kodları döndürmek gibi ancak kafa karıştırıcıdır çünkü kullanıcının dönüşünü kontrol etmesi gerekir. uygun istisnaları yakalamak yerine null, ör.
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
Bu, pek çok tasarım kararında olduğu gibi, mutlaka kötü bir tasarım değildir.
Yöntemin sonucu, normal kullanımda iyi bir sonucu olmayacak bir şeyse, null döndürmek iyidir:
object x = GetObjectFromCache(); // return null if it's not in the cache
Gerçekten her zaman boş olmayan bir sonuç olması gerekiyorsa, bir istisna atmak daha iyi olabilir:
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<>
Bazı senaryolar için, bir arızayı meydana gelir gelmez fark etmek istersiniz.
Arıza durumunda NULL'a karşı kontrol etmek ve iddia etmemek (programcı hataları için) veya atmamak (kullanıcı veya arayan hataları için), orijinal garip durum bulunamadığından sonraki çökmelerin izini sürmenin daha zor olduğu anlamına gelebilir.
Dahası, hataları göz ardı etmek, güvenlik açıklarına yol açabilir. Belki de boşluk, bir tamponun üzerine yazılması veya benzeri olgudan kaynaklanmıştır. Şimdi, vardır değil sömürücü kodunuzda yürütmek için bir şansı var demektir çarpıyor.
Boş değer döndürmek için hangi alternatifleri görüyorsunuz?
İki durum görüyorum:
Bu durumda şunları yapabiliriz: Null değerini döndürür veya bir (işaretli) istisna atar (veya belki bir öğe oluşturup onu iade eder)
Bu durumda Null döndürebilir, boş bir liste döndürebilir veya bir İstisna atabiliriz.
Geri dönüş boş değerinin alternatiflerden daha az iyi olabileceğine inanıyorum, çünkü istemcinin boş değeri kontrol etmeyi, programcıların unutmasını ve kodlamayı hatırlamasını gerektiriyor
x = find();
x.getField(); // bang null pointer exception
Java'da, kontrol edilen bir istisna olan RecordNotFoundException, derleyicinin istemciye vakayla ilgilenmesini hatırlatmasını sağlar.
Boş listeleri döndüren aramaların oldukça kullanışlı olabileceğini görüyorum - sadece ekranı listenin tüm içeriği ile doldurun, oh boş, kod "sadece çalışıyor".
Bazen, NULL döndürmek yapılacak doğru şeydir, ancak özellikle farklı tür dizilerle (diziler, listeler, dizeler, neye sahipseniz) uğraşırken sıfır uzunlukta bir dizi döndürmek muhtemelen daha iyidir. daha kısa ve umarız daha anlaşılır koda yol açarken, API uygulayıcısı adına çok fazla yazı yazmaz.
Önceki çağrının boş olup olmadığını anlamak için gerçeğin ardından başka bir yöntemi çağırmalarını sağlayın. ;-) Hey, JDBC için yeterince iyiydi
Bu iş parçacığının arkasındaki temel fikir, savunma amaçlı programlamaktır. Yani beklenmeyene karşı kodlayın. Bir dizi farklı yanıt var:
Adamski , bu öneriye verilen yanıtla birlikte Boş Nesne bakmayı öneriyor.
Michael Valenty ayrıca geliştiriciye ne beklenebileceğini anlatmak için bir adlandırma kuralı önerir. ZeroConcept , , düzgün bir şekilde kullanılmasını önerir. Ve diğerleri.
Her zaman savunmacı programlama yapmak istediğimiz "kuralı" koyarsak, bu önerilerin geçerli olduğunu görebiliriz.
Ancak 2 geliştirme senaryomuz var.
Bir geliştirici tarafından "yazılan" sınıflar: Yazar
Başka (belki) geliştirici tarafından "tüketilen" sınıflar: Geliştirici
Bir sınıfın dönüş değeri olan yöntemler için NULL döndürüp döndürmediğine bakılmaksızın, Geliştiricinin nesnenin geçerli olup olmadığını test etmesi gerekecektir.
Geliştirici bunu yapamazsa, o Sınıf / yöntem deterministik değildir. Yani, nesneyi elde etmek için "yöntem çağrısı", "reklamını yaptığı" şeyi yapmazsa (örneğin getEmployee) sözleşmeyi bozmuştur.
Bir sınıfın yazarı olarak, bir yöntem oluştururken her zaman nazik ve savunmacı (ve determinist) olmak isterim.
Dolayısıyla, NULL veya NULL OBJECT'in (örn. (NullEmployee.ISVALID olarak çalışan)) kontrol edilmesi gerektiği ve bunun bir Çalışan koleksiyonunda gerçekleşmesi gerekebileceği düşünüldüğünde, boş nesne yaklaşımı daha iyi bir yaklaşımdır.
Ama aynı zamanda Michael Valenty'nin null döndürmesi ZORUNLU yöntemi isimlendirme önerisini de seviyorum , örneğin getEmployeeOrNull.
Bir istisna atan Yazar, geliştiricinin nesnenin geçerliliğini test etme seçeneğini kaldırıyor; bu, bir nesneler koleksiyonunda çok kötüdür ve geliştiriciyi, tüketen kodlarını dallandırırken istisna işlemeye zorlar.
Dersi tüketen bir geliştirici olarak, umarım yazar bana sınıflarının / yöntemlerinin karşılaşabileceği boş durumdan kaçınma veya program yapma yeteneği verir.
Bu yüzden bir geliştirici olarak NULL'a karşı bir yöntemle savunma amaçlı programlama yapardım. Yazar bana her zaman bir nesne döndüren (BOŞ NESNE her zaman verir) bir sözleşme verdiyse ve bu nesnenin, nesnenin geçerliliğini test etmek için bir yöntemi / özelliği varsa, nesneyi kullanmaya devam etmek için bu yöntemi / özelliği kullanırdım. , aksi takdirde nesne geçerli değil ve onu kullanamam.
Sonuç olarak, Sınıfın / Yöntemlerin Yazarının, bir Geliştiricinin savunma programlarında kullanabileceği mekanizmaları sağlaması gerektiğidir. Yani, yöntemin daha net bir amacı.
Geliştirici, başka bir sınıftan / yöntemden döndürülen nesnelerin geçerliliğini test etmek için her zaman savunmacı programlama kullanmalıdır.
Saygılarımızla
GregJF
Bunun diğer seçenekleri şunlardır: başarıyı gösteren veya olmayan (veya bir hata türünü) gösteren bir değer döndürmek, ancak yalnızca başarı / başarısızlık belirtecek bir boole değerine ihtiyacınız varsa, başarısızlık için boş döndürmek ve başarı için bir nesne olmaz daha az doğru olmak, sonra doğru / yanlış döndürmek ve nesneyi parametre yoluyla almak.
Başka bir yaklaşım, başarısızlıkları belirtmek için istisnayı kullanmak olacaktır, ancak burada - aslında bunun KÖTÜ bir uygulama olduğunu söyleyen çok daha fazla ses var (çünkü istisnaları kullanmak uygun olabilir ancak birçok dezavantaja sahiptir).
Bu yüzden, ben şahsen, bir şeylerin ters gittiğinin göstergesi olarak boş döndürmede ve daha sonra kontrol etmede (gerçekten başarılı olup olmadığınızı bilmek için) kötü bir şey görmüyorum. Ayrıca, yönteminizin NULL döndürmeyeceğini ve daha sonra kodunuzu buna dayandıracağını körü körüne düşünmek, başka, bazen bulunması zor hatalara yol açabilir (ancak çoğu durumda sisteminizi çökertecektir :) 0x00000000 er ya da geç).
Karmaşık programların geliştirilmesi sırasında istenmeyen boş işlevler ortaya çıkabilir ve ölü kod gibi, bu tür olaylar program yapılarında ciddi kusurları gösterir.
Boş bir işlev veya yöntem, genellikle bir nesne çerçevesindeki revizyona tabi tutulabilir işlevin veya geçersiz kılınabilen yöntemin varsayılan davranışı olarak kullanılır.
Elbette yöntemin amacına bağlı ... Bazen daha iyi bir seçim bir istisna atmak olabilir. Her şey duruma göre değişir.
Kod şunun gibi bir şeyse:
command = get_something_to_do()
if command: # if not Null
command.execute()
Execute () yöntemi hiçbir şey yapmayan bir kukla nesneniz varsa ve uygun durumlarda Null yerine bunu döndürürseniz, Null durumunu kontrol etmeniz gerekmez ve bunun yerine şunları yapabilirsiniz:
get_something_to_do().execute()
Dolayısıyla, burada sorun NULL kontrolü ile istisna arasında değil, arayanın özel olmayan vakaları farklı şekilde (herhangi bir şekilde) ele alması ya da yapmaması arasındadır.
Kullanım durumum için yöntemden bir Harita döndürmem ve ardından belirli bir anahtarı aramam gerekiyordu. Ancak boş bir Harita döndürürsem, NullPointerException'a yol açacak ve boş bir Harita yerine boş döndüren çok farklı olmayacak. Ancak Java8'den itibaren İsteğe Bağlı'yı kullanabiliriz . Yukarıdakiler, İsteğe Bağlı konseptin tanıtılmasının tam sebebidir.
İyi günler,
Yeni bir nesne oluşturamadığınızda NULL döndürmek, birçok API için standart uygulamadır.
Neden kötü bir tasarım olduğu hakkında hiçbir fikrim yok.
Düzenleme: Bu, uzun yıllardır kongre olduğu C gibi istisnaların olmadığı diller için geçerlidir.
HTH
'Avahappy,