İddia kötülük mü? [kapalı]


199

GoDil yaratıcıları yazma :

Go iddialarda bulunmaz. İnkar edilemeyecek kadar kullanışlılar, ancak deneyimlerimiz, programcıların uygun hata işleme ve raporlama hakkında düşünmekten kaçınmak için bunları bir koltuk değneği olarak kullanmalarıydı. Doğru hata işleme, sunucuların çökme yerine ölümcül olmayan hatalardan sonra çalışmaya devam ettiği anlamına gelir. Doğru hata raporlaması, hataların doğrudan ve noktaya geldiği anlamına gelir ve programcının büyük bir kilitlenme izini yorumlamasını önler. Kesin hatalar, programcı hataları gören kodla aşina olmadığında özellikle önemlidir.

Bu konudaki fikriniz nedir?


4
teğet: Go alışılmadık biçimde düşünülmüş bir dildir. Bu mutlaka kötü bir şey değil. Bununla birlikte, görüşlerini daha büyük bir tuz tanesi ile almanız gerektiği anlamına gelir. Aynı zamanda, katılmıyorsanız, dili kullanırken dişlerinizi gıcırdayacağınız anlamına da gelir. Go'nun gerçeğe rağmen görüşlerine nasıl yapıştığının bir kanıtı olarak, iki koleksiyonun eşit olup olmadığını belirlemek için yansıma büyüsüne başvurmanız gerektiğini düşünün.
allyourcode

@allyourcode Eğer atıfta bulunuyorsanız reflect.DeepEqual, kesinlikle buna ihtiyacınız yoktur . Bu uygundur, ancak performans pahasına (birim testler iyi bir kullanım durumudur). Aksi takdirde, "koleksiyonunuz" için uygun olan eşitlik denetimini çok fazla sorun yaşamadan uygulayabilirsiniz.
Igor Dubinskiy

1
Hayır, bahsettiğim bu değil. Yansıma olmadan slice1 == slice2 diye bir şey yoktur. Diğer tüm diller bu süper temel işleme eşdeğerdir. Go'nun tek nedeni önyargıdır.
allyourcode

forGo'da bir döngü kullanarak iki dilimi yansımasız karşılaştırabilirsiniz (tıpkı C gibi). Olur işaretçileri ve yapılar dahil olduğunda karşılaştırma karmaşık bir hal alıyor rağmen, jenerik dilim işlemleri için gerçekten güzel.
kbolino

Yanıtlar:


321

Hayır, assertistediğiniz gibi kullandığınız sürece yanlış bir şey yoktur .

Yani, normal hata işlemenin aksine, hata ayıklama sırasında "gerçekleşemeyen" vakaları yakalamak için olması gerekiyordu.

  • Assert: Programın mantığındaki bir hata.
  • Hata İşleme: Programdaki bir hata nedeniyle hatalı giriş veya sistem durumu.

109

Hayır, ne gotoneassert kötülük . Ancak her ikisi de yanlış kullanılabilir.

Assert akıl sağlığı kontrolleri içindir. Doğru değilse programı öldürmesi gereken şeyler. Doğrulama için değil veya hata işleme için değil.


gotoakıllıca nasıl kullanılır ?
ar2015

1
@ ar2015 Bazı insanların gototamamen dini nedenlerden kaçınmayı önerdiği saçma sapan kalıplardan birini bulun , sonra sadece gotoyaptığınız şeyi gizlemek yerine kullanın . Başka bir deyişle: gerçekten ihtiyacınız olduğunu kanıtlayabiliyorsanız gotove tek alternatif, sonuçta aynı şeyi yapan ama Goto Polisini değiştirmeden bir sürü anlamsız iskele çıkarmaktır ... o zaman sadece kullanın goto. Tabii ki, bunun bir ön koşulu "gerçekten ihtiyacınız olduğunu kanıtlayabilirseniz" biraz goto. Çoğu zaman insanlar bunu yapmazlar. Bu hala doğası gereği Kötü Bir Şey olduğu anlamına gelmez.
underscore_d

2
gotolinux çekirdeğinde kod temizleme için kullanılır
malat

61

Bu mantıkla kesme noktaları da kötülüktür.

Ekler, hata ayıklama yardımcısı olarak kullanılmalıdır ve başka bir şey kullanılmamalıdır. "Kötülük" onları kullanmayı denediğiniz zamandır hata işleme zamandır.

Varlıklar, programcıya, var olmaması gereken sorunları tespit edip düzeltmenize ve varsayımlarınızın doğru kaldığını doğrulamanıza yardımcı olmak için vardır.

Hata işleme ile hiçbir ilgileri yoktur, ancak ne yazık ki, bazı programcılar onları bu şekilde kötüye kullanır ve daha sonra "kötülük" ilan eder.


40

Assert kullanmayı çok seviyorum. Uygulamaları ilk kez oluştururken (belki de yeni bir alan için) çok yararlı buluyorum. (Erken optimizasyon düşünün) çok süslü hata denetimi yapmak yerine hızlı kod ve ekleri bir sürü ekleyin. İşlerin nasıl çalıştığı hakkında daha fazla bilgi edindikten sonra, bir yeniden yazma işlemi gerçekleştirir ve bazı ekleri kaldırır ve daha iyi hata işleme için değiştiririm.

Ekler nedeniyle kodlama / hata ayıklama programlarına çok daha az zaman harcıyorum.

Ayrıca, programların kırılabilecek birçok şeyi düşünmemde bana yardımcı olduğunu fark ettim.


31

Ek bir bilgi olarak go, yerleşik bir işlev sağlar panic. Bu yerine kullanılabilir assert. Örneğin

if x < 0 {
    panic("x is less than 0");
}

panicyığın izini yazdırır, böylece bir şekilde amacı vardır assert.


30

Programdaki hataları tespit etmek için kullanılmalıdırlar. Kötü kullanıcı girişi değil.

Doğru kullanılırsa, bunlar değil kötülük.


13

Bu çok ortaya çıkıyor ve bence iddiaların savunmasını kafa karıştırıcı yapan bir problem, genellikle argüman kontrolüne dayanıyor olmaları. Bir iddiayı ne zaman kullanabileceğinize ilişkin bu farklı örneği göz önünde bulundurun:

build-sorted-list-from-user-input(input)

    throw-exception-if-bad-input(input)

    ...

    //build list using algorithm that you expect to give a sorted list

    ...

    assert(is-sorted(list))

end

Girdi için bir istisna kullanıyorsunuz, çünkü bazen kötü girdi alacağınızı umuyorsunuz. Listenin, algoritmanızda tanımladığınız bir hata bulmanıza yardımcı olacak şekilde sıralandığını iddia edersiniz. İddia sadece hata ayıklama yapısındadır, bu nedenle kontrol pahalı olsa da, rutinin her bir çağrısında bunu yapmanın bir sakıncası yoktur.

Üretim kodunuzu yine de birim test etmeniz gerekiyor, ancak bu, kodunuzun doğru olduğundan emin olmanın farklı ve tamamlayıcı bir yoludur. Birim testleri, rutininizin arayüzüne uygun olmasını sağlarken, iddialarınız, uygulamanızın tam olarak beklediğiniz şeyi yaptığından emin olmak için daha ayrıntılı bir yöntemdir.


8

İddialar kötü değildir, ancak kolayca kötüye kullanılabilirler. "Doğru hata işleme ve raporlama hakkında düşünmekten kaçınmak için iddiaların sık sık koltuk değneği olarak kullanıldığı" ifadesine katılıyorum. Bunu oldukça sık gördüm.

Şahsen, kodlarımı yazarken yapmış olabileceğim varsayımları belgelediğinden iddiaları kullanmayı seviyorum. Kod korunurken bu varsayımlar bozulursa, sorun test sırasında tespit edilebilir. Ancak, ben bir üretim inşa (yani, #ifdefs kullanarak) yaparken benim kod her iddia dışarı sıyırma noktası yapmak. Üretim yapısındaki iddiaları çıkararak, onları koltuk değneği olarak kötüye kullanma riskini ortadan kaldırıyorum.

İddialarla ilgili başka bir sorun daha var. İddialar sadece çalışma zamanında kontrol edilir. Ancak genellikle yapmak istediğiniz kontrolün derleme zamanında yapılmış olabileceği düşünülmektedir. Bir sorunun derleme zamanında tespit edilmesi tercih edilir. C ++ programcıları için boost, bunu yapmanızı sağlayan BOOST_STATIC_ASSERT sağlar. C programcıları için, bu makalede ( bağlantı metni ) derleme zamanında iddiaları gerçekleştirmek için kullanılabilecek bir teknik açıklanmaktadır.

Özet olarak, takip ettiğim temel kural şudur: Bir üretim yapısında iddiaları kullanmayın ve mümkünse yalnızca derleme zamanında doğrulanamayan şeyler için iddiaları kullanın (yani, çalışma zamanında kontrol edilmelidir).


5

Uygun hata bildirimi göz önüne alınmadan ekler kullandığını itiraf ediyorum. Ancak, doğru kullanıldıklarında çok yardımcı olduklarını ortadan kaldırmazlar.

Özellikle "Crash Early" ilkesini takip etmek istiyorsanız özellikle kullanışlıdır. Örneğin, bir referans sayma mekanizması uyguladığınızı varsayalım. Kodunuzdaki belirli yerlerde yeniden sayımın sıfır veya bir olması gerektiğini bilirsiniz. Ayrıca, yeniden sayım yanlışsa programın hemen çökmeyeceğini, ancak bir sonraki mesaj döngüsünde hangi noktanın yanlış gittiğini bulmanın zor olacağını varsayalım. Bir iddia, hatanın kaynağına yakın tespit edilmesinde yardımcı olacaktır.


5

Hata ayıklama ve sürümde farklı şeyler yapan kod kaçınmayı tercih ederim.

Hata ayıklayıcıyı bir koşulda kırmak ve tüm dosya / satır bilgilerine sahip olmak, aynı zamanda tam ifade ve tam değer de yararlıdır.

"Durumu sadece hata ayıklamada değerlendirecek" bir iddiaya sahip olmak, bir performans optimizasyonu olabilir ve bu nedenle, programların sadece% 0.0001'inde yararlı olabilir - insanların ne yaptığını bildiği yerlerde. Diğer tüm durumlarda bu zararlıdır, çünkü ifade aslında programın durumunu değiştirebilir:

assert(2 == ShroedingersCat.GetNumEars()); Programın hata ayıklama ve bırakmada farklı şeyler yapmasını sağlar.

Bir istisna atacak ve hem hata ayıklama hem de yayın sürümünde yapacak bir dizi onaylama makrosu geliştirdik. Örneğin, THROW_UNLESS_EQ(a, 20);ne () iletisinin hem dosya, satır hem de gerçek değerlerine sahip olduğu bir istisna atar . Sadece bir makronun buna gücü vardır. Hata ayıklayıcı, belirli kural dışı durum türünün 'atışında' kırılmak üzere yapılandırılabilir.


4
Bağımsız değişkenlerde kullanılan istatistiklerin% 90'ı yanlıştır.
João Portela

5

Ben şiddetle sevmiyorum sevmiyorum. Gerçi kötü olduklarını söyleyecek kadar ileri gitmem.

Temel olarak, bir iddia sahibi kontrol edilmeyen bir istisna ile aynı şeyi yapacaktır, tek istisna, onaylayıcının (normalde) nihai ürün için saklanmaması gerektiğidir.

Sistemde hata ayıklama ve oluşturma sırasında kendiniz için bir güvenlik ağı oluşturursanız, bu güvenlik ağını neden müşteriniz, destek yardım masanız veya şu anda oluşturduğunuz yazılımı kullanacak olanlar için inkar edersiniz. İstisnaları yalnızca hem varsayımlar hem de istisnai durumlar için kullanın. Uygun bir istisna hiyerarşisi oluşturarak birini diğerinden çok çabuk fark edebileceksiniz. Bu süre zarfında iddia eden kişi yerinde kalır ve aksi takdirde kaybedilecek bir arıza durumunda değerli bilgiler sağlayabilir.

Bu yüzden, Go'yu oluşturanları, varsayımları tamamen kaldırarak ve programcıları durumu ele almak için istisnalar kullanmaya zorlayarak tam olarak anlıyorum. Bunun basit bir açıklaması var, istisna sadece arkaik iddialara sadık kalmak için iş için daha iyi bir mekanizmadır?


Go'nun istisnaları yoktur. Bir istisna yerine bir assert kullanmanın genel nedeni, performans nedeniyle dağıtımda kullanılmasını istemenizdir. Ekler arkaik değildir. Kulağa sert geldiğim için üzgünüm, ama bu cevap orijinal soruya uzaktan yardımcı değil, doğru da değil.
Nir Friedman


3

Son zamanlarda koduma bazı ekler eklemeye başladım ve ben de bunu yapıyorum:

Zihnimi zihinsel olarak sınır koduna ve iç koda bölerim. Sınır kodu, kullanıcı girişini işleyen, dosyaları okuyan ve ağdan veri alan koddur. Bu kodda, yalnızca girdi geçerli olduğunda (etkileşimli kullanıcı girdisi durumunda) çıkan bir döngüde giriş ister veya kurtarılamayan dosya / ağ bozuk verileri durumunda istisnalar atarım.

Dahili kod her şeydir. Örneğin, sınıfımda bir değişken ayarlayan bir işlev şu şekilde tanımlanabilir:

void Class::f (int value) {
    assert (value < end);
    member = value;
}

ve bir ağdan girdi alan bir işlev şöyle okunabilir:

void Class::g (InMessage & msg) {
    int const value = msg.read_int();
    if (value >= end)
        throw InvalidServerData();
    f (value);
}

Bu bana iki kat kontrol sağlıyor. Verilerin çalışma zamanında belirlendiği her şey her zaman bir istisna veya anında hata işleme alır. Ancak, ifadeyle Class::fyapılan bu ekstra kontrol, assertbazı iç kodların çağrılması durumunda Class::f, hala bir sağlık kontrolü yaptığım anlamına gelir . Dahili kodum geçerli bir argüman iletmeyebilir (çünkü valuebazı karmaşık fonksiyon serilerinden hesaplamış olabilirim ), bu yüzden fonksiyonu kimin çağırdığına bakılmaksızın, ayar fonksiyonundaki iddianın valuedaha büyük veya eşittir end.

Bu, birkaç yerde okuduğum şeye uyuyor gibi görünüyor, iyi işleyen bir programda iddiaların ihlal edilmesi imkansız olmalı, istisnalar hala mümkün olan istisnai ve hatalı durumlar için olmalıdır. Teoride tüm girdileri doğruladığım için iddiamın tetiklenmesi mümkün olmamalı. Eğer öyleyse, programım yanlış.


2

Eğer bahsettiğiniz varsayımlar programın kusup sonra var olduğu anlamına gelirse, iddialar çok kötü olabilir. Bu her zaman oldukları anlamına gelmez kullanmak için yanlış bir şey oldukları anlamına gelmez, çok kolay kötüye kullanılan bir yapıdır. Ayrıca çok daha iyi alternatifleri var. Bunun gibi şeyler kötülük olarak adlandırılmak için iyi adaylardır.

Örneğin, 3. taraf bir modül (veya herhangi bir modül) çağıran programdan neredeyse hiç çıkmamalıdır. Bu, çağıran programcıya, programın o anda alması gereken risk üzerinde herhangi bir kontrol sağlamaz. Çoğu durumda, veriler o kadar önemlidir ki, bozuk verilerin kaydedilmesi bile bu verileri kaybetmekten daha iyidir. Ekler sizi veri kaybetmeye zorlayabilir.

İddialara bazı alternatifler:

  • Bir hata ayıklayıcı kullanarak,
  • Konsol / veritabanı / diğer günlük kaydı
  • İstisnalar
  • Diğer hata işleme türleri

Bazı referanslar:

İddiayı savunan insanlar bile üretimde değil , yalnızca gelişimde kullanılmaları gerektiğini düşünmektedir :

Bu kişi, modülün bir istisna atıldıktan sonra devam eden potansiyel olarak bozuk veriler olduğunda kullanılması gerektiğini söylüyor: http://www.advogato.org/article/949.html . Bu kesinlikle makul bir noktadır, ancak harici bir modül asla çağrılan program için bozuk verilerin ne kadar önemli olduğunu reçete etmemelidir (onlar için "çıkarak"). Bunu ele almanın doğru yolu, programın şimdi tutarsız bir durumda olabileceğini açıkça belirten bir istisna atmaktır. İyi programlar çoğunlukla modüller içerdiğinden (ana yürütülebilir dosyada küçük bir tutkal kodu ile), iddialar hemen hemen her zaman yanlış şeydir.


1

assert çok kullanışlıdır ve beklenmedik hatalar meydana geldiğinde programı ilk sorun belirtilerinde durdurarak size çok fazla geri izleme kaydedebilirsiniz.

Öte yandan, kötüye kullanımı çok kolaydır assert.

int quotient(int a, int b){
    assert(b != 0);
    return a / b;
}

Doğru, doğru sürüm şöyle bir şey olacaktır:

bool quotient(int a, int b, int &result){
    if(b == 0)
        return false;

    result = a / b;
    return true;
}

Yani ... uzun vadede ... büyük resimde ... assertbunun kötüye kullanılabileceğini kabul etmeliyim . Onu her zaman yaparım.


1

assert daha az yazdığı için hata işleme nedeniyle kötüye kullanılıyor.

Dolayısıyla, dil tasarımcıları olarak, daha az yazarak doğru hata işlemenin yapılabileceğini görmelidirler. İstisna mekanizmanızın ayrıntılı olması nedeniyle iddiayı hariç tutmak çözüm değildir. Oh bekleyin, Go'nun da istisnaları yok. Çok kötü :)


1
Çok kötü değil :-) İstisnalar ya da değil, iddialar ya da değil, Go hayranları hala kodların ne kadar kısa olduğundan bahsediyorlar.
Moshe Revah

1

Bunu görünce kafamda yazar tekmelemek gibi hissettim.

Ben kod her zaman ekler kullanın ve ben daha fazla kod yazarken sonunda hepsini değiştirin. Gerekli mantığı yazmadım ve proje tamamlanmaya yaklaştıkça silinecek bir istisna yazmak yerine koda girdiğimde uyarılmak istiyorum.

İstisnalar, sevmediğim üretim koduyla daha kolay uyum sağlar. Bir iddiayı farketmekten daha kolaydırthrow new Exception("Some generic msg or 'pretend i am an assert'");


1

Bu cevapları savunmayı savunanlarla ilgili sorunum, kimsenin onu normal bir ölümcül hatadan neyin farklı kıldığını ve neden bir savunucunun bir istisnanın alt kümesi olamayacağını açıkça belirtmemesi . Şimdi, bununla birlikte, ya istisna asla yakalanmazsa? Bu isimlendirme ile bir iddia mı ediyor? Ve neden, dilde / hiçbir şeyin / işleyemeyeceği bir istisna doğurabileceğine dair bir kısıtlama getirmek istesin ki?


Cevabıma bakarsan. Benim kullanım ben tutmak istisnalar vs hata ayıklama için kullanılan kurtulmak istiyorum 'istisnalar' (iddialar) ayırt etmektir. Neden onlardan kurtulmak isteyeyim ki? çünkü onlar olmadan çalışma tam olmazdı. Örnek olarak ben 3 vakayı ele alıyorum ve 4. vakayı todo yapıyoruz. Kolayca kod onları bulmak için eksik iddia ve onun eksik bilmek yerine daha sonra yanlışlıkla (başka bir programcı tarafından) yakalanmış olabilir ya da ben bir istisna ya da kod çözmek gerekir bir mantık denetimi olmadığını söylemek zor bir istisna kullanın.

Benim gözümde, bu "mühürlü" sınıflarla aynı seviyede ve aynı nedenden ötürü kötü bir fikir. Saklamak istediğiniz istisnaların, henüz bilmediğiniz kodunuzun kullanımları için kabul edilebilir olduğunu varsayıyorsunuz. Tüm istisnalar aynı kanallardan geçer ve kullanıcı onları yakalamak istemiyorsa yapmamayı tercih edebilir. Eğer yaparsa, bu yeteneğe de sahip olmalıdır. Her iki durumda da, sadece varsayımlarda bulunuyorsunuz ya da iddianıza benzer bir kavramla uygulamanızı itiyorsunuz.
Evan Carroll

Örnek senaryoların en iyisi olduğuna karar verdim. Heres basit bir tane. int func (int i) {if (i> = 0) {console.write ("Sayı pozitif {0}", i); } else {assert (false); // negatif ATM yapmak için tembel olmak} i i * 2 döndür; <- Bunu iddia etmeden nasıl yaparım ve bir istisna gerçekten daha iyi? ve unutmayın, bu kod yayınlanmadan önce uygulanacaktır.

Tabii istisnalar daha iyidir, diyelim ki kullanıcı girişi alıyorum ve func()negatif bir numara ile çağrı yapıyorum . Şimdi, iddialarınızla aniden halıyı altımdan çıkardınız ve bana iyileşme şansı vermediniz, kibarca bana ne istediğimi söyleyemezsiniz. Programı yanlış davranmakla ve bir alıntı yapmakla suçlamanın yanlış bir yanı yoktur: Sorun şu ki, bir yasayı uygulama ve suçu cezalandırma eylemini bulanıklaştırıyorsunuz.
Evan Carroll

Bu noktada, kullanıcının ne yapmak istediğini söyleyemezsiniz. Uygulama hata msj ile sona ermelidir ve kim hata ayıklama onun YAPILMAMALIDIR bir şey hatırlıyorum ama değil. Onun ele alınmasını İSTEMİYORSUNUZ. Bir fesih istiyorsunuz ve bu davayı ele almadığınızı hatırlatmak istersiniz. ve yapmanız gereken tek şey kodda bir iddiayı aramak olduğu için hangi vakaların kaldığını görmek son derece kolaydır. Kodun üretime hazır olduğunda bunu yapabilmesi gerektiğinden hatayı işlemek yanlış olur. Bir programcının onu yakalamasına ve başka bir şey yapmasına izin vermek yanlıştır.

1

Evet, iddialar kötüdür.

Genellikle uygun hata işlemenin kullanılması gereken yerlerde kullanılırlar. Başlangıçtan itibaren uygun üretim kalitesi hata işlemeye yazmaya alışın!

Genellikle birim testleri yazma yoluna girerler (test kemerinizle etkileşime giren özel bir onay yazmazsanız). Bunun nedeni genellikle uygun hata işlemenin kullanılması gereken yerlerde kullanılmasıdır.

Çoğunlukla yayın yapılarından derlenirler; bu, gerçekten yayınladığınız kodu çalıştırdığınızda "testlerinin" hiçbirinin kullanılamayacağı anlamına gelir; Çok iş parçacıklı durumlarda en kötü sorunların genellikle yalnızca sürüm kodunda gösterilmesi kötü olabilir.

Bazen başka türlü kırık tasarımlar için bir koltuk değneği; yani kodun tasarımı bir kullanıcının kodu çağrılmaması gereken bir şekilde çağırmasına izin verir ve iddia eden kişi bunu "önler". Tasarımı düzeltin!

Bu konuyu 2005 yılında blogumda yazdım: http://www.lenholgate.com/blog/2005/09/assert-is-evil.html


0

Genel olarak ters verim kadar kötü değil. Kalıcı hata kontrolü ile hata ayıklama arasında bir ayrım vardır. Assert, tüm hata ayıklama işlemlerinin kalıcı olması gerektiğini düşünmesini sağlar ve çok kullanıldığında büyük okunabilirlik sorunlarına neden olur. Kalıcı hata işleme, gerektiğinden daha iyi olmalıdır ve iddia kendi hatalarına neden olduğu için oldukça tartışmalı bir uygulamadır.


5
assert, bir fonksiyonun üstündeki ön koşulları bildirmek için iyidir ve açıkça yazılmışsa, fonksiyonun belgelerinin bir parçası olarak hareket eder.
Peter Cordes

0

Asla assert () kullanın, örnekler genellikle böyle bir şey göstermek:

int* ptr = new int[10];
assert(ptr);

Bu kötü, bunu asla yapmam, ya oyunum bir sürü canavar tahsis ederse? neden oyunu çökmeli, bunun yerine hataları incelikle ele almalısınız, bu yüzden şöyle bir şey yapın:

CMonster* ptrMonsters = new CMonster[10];
if(ptrMonsters == NULL) // or u could just write if(!ptrMonsters)
{
    // we failed allocating monsters. log the error e.g. "Failed spawning 10 monsters".
}
else
{
    // initialize monsters.
}

14
newasla geri dönmez nullptr, atar.
David Stone

Bunun için std :: nothrow kullanabileceğinizi unutmayın .
markand
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.