Normal yolu izlemeli miyim yoksa erken mi başarısız olmalıyım?


73

Gönderen Kod tam kitabında aşağıdaki alıntı gelir:

"Normal davaya sıradan sonra ifdeğil else"

Bu, standart yoldan istisnaların / sapmaların elsekasaya konması gerektiği anlamına gelir .

Fakat Pragmatik Programcı bize “erken çökmeyi ” öğretti (s. 120).

Hangi kurala uymalıyım?


15
@gnat kopya değil
BЈовић

16
ikisi birbirini dışlayan değil, belirsizlik yoktur.
14’te jwenting

6
Kod tamamlama teklifinin çok iyi bir tavsiye olduğundan emin değilim. Muhtemelen okunabilirliği arttırma girişimidir, ancak nadir görülen vakaların ilk önce metinsel olmasını istediğiniz durumlar vardır. Bunun bir cevap olmadığını unutmayın; Mevcut cevaplar, sorunuzun neden olduğu karışıklığı zaten açıklıyor.
Eamon Nerbonne

14
Çarpışma erken, çarpışma zor! ifŞubelerinizden biri geri dönerse, önce onu kullanın. Ve elsekodun geri kalanı için önlemek, önceden koşullar başarısız olursa zaten iade etmişsinizdir. Kod okuması daha kolay, daha az girinti ...
CodeAngry

5
Bunun okunabilirlikle ilgisi olmadığını düşünen sadece ben miyim, ama bunun yerine statik dal tahmini için optimizasyonda yanlış yönlendirilmiş bir girişim olabilirdi.
Mehrdad

Yanıtlar:


189

"Crash early", hangi kod satırının metinsel olarak daha erken geldiği ile ilgili değildir. Size mümkün olan en erken işlem adımındaki hataları tespit etmenizi söyler , böylece yanlışlıkla hatalı duruma dayalı kararlar ve hesaplamalar yapmazsınız.

Bir if/ elseyapıda, bloklardan yalnızca birinin yürütüldüğü için "erken" veya "daha sonra" bir adım oluşturduğu söylenemez. Bu yüzden nasıl sipariş verilebileceği okunabilirlik meselesidir ve “erken başarısız” kararı vermez.


2
Genel amacın harika ama bir konu görüyorum. Eğer varsa (if / else if / else), ifade "else if" de, "if" deyimindeki ifadeden sonra değerlendirilir. Belirttiğiniz gibi önemli olan, okunabilirliğe karşı kavramsal işlem birimidir. Yalnızca bir kod bloğu yürütülür, ancak birden fazla koşul değerlendirilebilir.
Encaitar

7
@Encaitar, bu ayrıntı düzeyi, "erken başarısız" ifadesi kullanıldığında genellikle amaçlanandan çok daha küçüktür.
riwalk,

2
@Encaitar Bu programlama sanatıdır. Birden fazla kontrolle karşılaşıldığında, ilk önce gerçek olması en muhtemel olanı denemek gerekir. Ancak, bu bilgiler tasarım zamanında bilinmeyebilir, optimizasyon aşamasında biliniyor olabilir, ancak erken optimizasyona dikkat edin.
BPugh

Adil yorumlar. Bu iyi bir cevaptı ve sadece ileride
başvurmak

JavaScript, Python, perl, PHP, bash, vb. Gibi betik dilleri istisnalar çünkü doğrusal olarak yorumlanıyorlar. Küçük if/elseyapılarda muhtemelen önemi yoktur. Ancak bir döngüde veya her blokta birçok ifadeyle çağrılanlar ilk önce en yaygın koşulla daha hızlı çalışabilir.
DocSalvager

116

Senin Eğer elsebeyanı sonra sadece başarısızlık kodunu içeren, büyük olasılıkla, bu olmamalıdır.

Bunu yapmak yerine:

if file.exists() :
  if validate(file) :
    # do stuff with file...
  else :
    throw foodAtMummy
else :
  throw toysOutOfPram

Bunu yap

if not file.exists() :
  throw toysOutOfPram

if not validate(file) :
  throw foodAtMummy

# do stuff with file...

Hata kontrolünü eklemek için kodunuzu derinlemesine yerleştirmek istemezsiniz.

Ve herkesin daha önce de söylediği gibi, iki tavsiyede bulunma çelişkili değildir. Biri yürütme emriyle ilgili , diğeri kod emriyle ilgili .


4
Eğer varsa bloktan sonra normal akış ve blokta ifistisnai bir akış olması için bu tavsiyede bulunma konusunda açık olunmaya değer ! Bunun gibi koruyucu ifadeler, çoğu kodlama stilinde hata koşullarını işlemek için tercih edilen formdur. elseelse
Jules

+1 bu iyi bir nokta ve aslında hata koşullarına sahip eşyaların nasıl sipariş edileceğine dair gerçek soruya bir cevap veriyor.
ashes999

Kesinlikle çok daha net ve bakımı kolaydır. Bu şekilde yapmayı seviyorum.
John

27

İkisini de takip etmelisin.

"Crash early" / erken başarısız tavsiyesi, girdilerinizi mümkün olan en kısa sürede test etmeniz gerektiği anlamına gelir.
Örneğin, yönteminiz pozitif olduğu düşünülen bir boyut veya sayı kabul ederse (> 0), başarısızlık erken önerisi, algoritmanın saçma saptamasını beklemek yerine, yöntemin başında bu koşulu test ettiğiniz anlamına gelir. Sonuçlar.

İlk önce normal davayı koymak için verilen tavsiye, bir durumu test ederseniz, en muhtemel yolun önce gelmesi gerektiği anlamına gelir. Bu, performansa (işlemcinin dal tahmini daha doğru olacağı için) ve okunabilirliğe yardımcı olur, çünkü normal durumda işlevin ne yaptığını anlamaya çalışırken kod bloklarını atlamak zorunda kalmazsınız.
Bu öneri bir ön koşulu sınamak ve derhal serbest if (!precondition) throwbırakmak (varsayımlar veya yapılar kullanarak ) için geçerli değildir, çünkü kodu okurken atlamak için herhangi bir hata yoktur.


1
Şube tahmin bölümünü inceleyebilir misiniz? Başka bir davaya gitme olasılığı daha yüksek olan koddan daha hızlı çalışacaksa, if dosyasına gitme olasılığı daha yüksek olan kod beklemem. Demek istediğim, bu dal tahmininin esas noktası, değil mi?
Roman Reiner

@ user136712: Modern (hızlı) işlemcilerde, önceki komut işlem tamamlanmadan önce talimatlar alınır. Şube tahmini, koşullu bir şube yürütülürken alınan talimatların da yürütülmesi gereken doğru talimat olma olasılığını artırmak için kullanılır.
Bart van Ingen Schenau

2
Branş tahmininin ne olduğunu biliyorum. Gönderinizi doğru okuduğumda bunun şube tahmini nedeniyle olduğundan if(cond){/*more likely code*/}else{/*less likely code*/}daha hızlı çalıştığını söylersiniz if(!cond){/*less likely code*/}else{/*more likely code*/}. Şube tahmininin ifadeye ifya da elseifadeye taraflı olmadığını ve sadece tarihi göz önünde bulunduracağını düşünüyorum. Dolayısıyla else, gerçekleşmesi daha muhtemel ise , bunun kadar iyi olduğunu tahmin edebilmelidir. Bu varsayım yanlış mı?
Roman Reiner

18

Sanırım @ JackAidley , bunun özünü zaten söyledi , ama şunu şöyle formüle edeyim:

İstisnasız (örneğin, C)

Düzenli kod akışında şunlara sahip olursunuz:

if (condition) {
    statement;
} else if (less_likely_condition) {
    less_likely_statement;
} else {
    least_likely_statement;
}
more_statements;

“Erken çıkış hatası” durumunda, kodunuz aniden şunu okur:

/* demonstration example, do NOT code like this */
if (condition) {
    statement;
} else {
    error_handling;
    return;
}

Bu kalıbı - a (ya da hatta ) bloğun returniçinde görürseniz , söz konusu kodun bir bloğu olmaması için derhal yeniden işleyin :elseifelse

/* only code like this at University, to please structured programming professors */
function foo {
    if (condition) {
        lots_of_statements;
    }
    return;
}

Gerçek dünyada…

/* code like this instead */
if (!condition) {
    error_handling;
    return;
}
lots_of_statements;

Bu iç içe çok fazla önler ve “erken patlak” Dava (zihin tutmaya yardımcı olur - ve kod akışını - temiz) yerine getirir ve “içine daha muhtemel şey koymak ihlal etmediğini ifsadece hayır olduğundan parçası” elsebölümü .

C ve temizleme

Benzer bir soruya (bu yanlış olana) cevaptan esinlenerek, C ile nasıl temizlik yaptığınızı işte burada bir veya iki çıkış noktasını kullanabilirsiniz, işte iki çıkış noktası için bir tane:

struct foo *
alloc_and_init(size_t arg1, int arg2)
{
    struct foo *res;

    if (!(res = calloc(sizeof(struct foo), 1)))
        return (NULL);

    if (foo_init1(res, arg1))
        goto err;
    res.arg1_inited = true;
    if (foo_init2(&(res->blah), arg2))
        goto err;
    foo_init_complete(res);
    return (res);

 err:
    /* safe because we use calloc and false == 0 */
    if (res.arg1_inited)
        foo_dispose1(res);
    free(res);
    return (NULL);
}

Yapılacak daha az temizleme işlemi varsa, bunları bir çıkış noktasına daraltabilirsiniz:

char *
NULL_safe_strdup(const char *arg)
{
    char *res = NULL;

    if (arg == NULL)
        goto out;

    /* imagine more lines here */
    res = strdup(arg);

 out:
    return (res);
}

gotoBununla başa çıkabiliyorsanız, bu kullanım tamamen iyidir; kullanımdan uzak durma tavsiyesi, gotokullanımın iyi, kabul edilebilir, kötü, makarna kodu veya başka bir şey olup olmadığına henüz karar veremeyen insanlara yöneliktir.

İstisnalar

Yukarıdakiler, kendimi çok tercih ettiğim istisnalar dışındaki dillerden bahsediyor (açık hata işlemlerini daha iyi ve daha az sürprizle yapabilirim). İgli'den alıntı yapmak için:

<igli> exceptions: a truly awful implementation of quite a nice idea.
<igli> just about the worst way you could do something like that, afaic.
<igli> it's like anti-design.
<mirabilos> that too… may I quote you on that?
<igli> sure, tho i doubt anyone will listen ;)

Ancak burada, istisnalar dışında bir dilde nasıl iyi yaptığınıza ve bunları iyi kullanmak istediğinizde:

istisnalar karşısında hata iadesi

returnBir istisna atmakla erken saatlerin çoğunu değiştirebilirsiniz . Ancak , sizin , normal herhangi bir kod akışının yani program akış, hangi program, iyi, bir istisna ... bir hata durumu karşılaşabilir veya bloklarýnýn etmedi etmeyecektir herhangi istisnalar yükseltmek.

Bu şu demek…

# this page is only available to logged-in users
if not isLoggedIn():
    # this is Python 2.5 style; insert your favourite raise/throw here
    raise "eh?"

… Tamam, ama…

/* do not code like this! */
try {
    openFile(xyz, "rw");
} catch (LockedException e) {
    return "file is locked";
}
closeFile(xyz);
return "file is not locked";

… değil. Temel olarak bir istisna, bir kontrol akışı elemanı değildir . Bu aynı zamanda Operasyonların size garip görünmesini sağlar (“bu Java ™ programcıları bize her zaman bu istisnaların normal olduğunu söyler”) ve hata ayıklamayı engelleyebilir (örneğin, IDE'ye herhangi bir istisnayı kırmasını söyleyin). İstisnalar, genellikle çalışma zamanı ortamının yığın geri izleme, vs. üretmek için çözülmesini gerektirir. Buna karşı muhtemelen daha fazla sebep vardır.

Bu, aşağıdakilere dayanır: istisnaları destekleyen bir dilde, mevcut mantık ve stille eşleşen ve doğal hisseden her şeyi kullanın. Sıfırdan bir şeyler yazıyorsanız, erken kararlaştırın. Sıfırdan bir kütüphane yazıyorsanız, tüketicilerinizi düşünün. ( abort()Kütüphanede de kullanmayın …) Ama ne yaparsanız yapın, kural olarak, işlem normal olarak devam ederse (az ya da çok) devam ederse, bir istisna oluşturmayın.

genel tavsiye İstisnalar

Önce tüm geliştirme ekibi tarafından kararlaştırılan İstisnalar'ın program içi kullanımını kullanmaya çalışın. Temel olarak, onları planlayın. Onları bolca kullanmayın. Bazen, C ++, Java ™, Python'da bile hata geri dönüşü daha iyidir. Bazen değil; onları düşünce ile kullanın.


Genelde erken dönüşleri kod kokusu olarak görüyorum. Bir önkoşul yerine getirilmediği için aşağıdaki kod başarısız olursa bunun yerine bir istisna atardım. Sadece söylüyorum
DanMan

1
@DanMan: Makalem C düşünülerek yazılmıştır… Normalde İstisnaları kullanmam. Ancak makaleyi (ayy, oldukça uzadı) öneri wrt ile genişlettim. İstisnalar; tesadüfen, dün şirketin iç posta listesinde de aynı soruyu
sorduk

Ayrıca, tek satırlı bile olsa parantez kullanın. goto fail;Girintide başka bir gizlenmiş istemiyorsun .
Bruno Kim

1
@BrunoKim: Bu tamamen birlikte çalıştığınız projenin stil / kodlama kuralına bağlıdır. Bunun üzerine kaşlarını çattığı BSD ile çalışıyorum (daha fazla optik dağınıklık ve dikey alan kaybı); $ dayjob, ancak ben kararlaştığımız gibi onları yerleştirin (yeni başlayanlar için daha az zor, daha az hata şansı, vb dediğiniz gibi).
mirabilos

3

Bence 'Guard Condition', kodu okunabilir hale getirmenin en iyi ve en kolay yollarından biri. ifYöntemin başlangıcında gördüğümde elseve ekranda görünmediğinden kodu görememekten nefret ediyorum . Sadece görmek için aşağı kaydırmam gerekiyor throw new Exception.

Çekleri başlangıca yerleştirin, böylece okuyan kişi okuma yönteminin her yerine atlamak zorunda kalmaz, bunun yerine daima yukarıdan aşağıya doğru tarayın.


2

(@mirabilos'un cevabı mükemmel, ama işte aynı sonuca varma sorusu hakkında nasıl düşünüyorum :)

Daha sonra fonksiyonumun kodunu okurken kendimi (veya başka birini) düşünüyorum. İlk satırı okuduğumda, girişim hakkında herhangi bir varsayımda bulunamam (zaten kontrol etmeyeceğim olanlar hariç). Bu yüzden benim düşüncem "Tamam, ben argümanlarımla bir şeyler yapacağımı biliyorum. Ama önce onları 'temizleyelim' - yani onların hoşuma gitmediği kontrol yollarını öldürün." Ama aynı zamanda , Normal vakayı şartlandırılmış bir şey olarak görmüyorum, normal olduğunu vurgulamak istiyorum.

int foo(int* bar, int baz) {

   if (bar == NULL) /* I don't like you, leave me alone */;
   if (baz < 0) /* go away */;

   /* there, now I can do the work I came into this function to do,
      and I can safely forget about those if's above and make all 
      the assumptions I like. */

   /* etc. */
}

-3

Bu tür bir koşul siparişi, söz konusu kod bölümünün eleştirisine ve kullanılabilecek varsayılanların olup olmadığına bağlıdır.

Başka bir deyişle:

A. kritik bölüm ve varsayılan yok => Erken Başarısız

B. kritik olmayan bölüm ve varsayılanlar => Varsayılanları başka bir bölümde kullan

C. vakalar arasında => gerektiğinde vaka başına karar ver


Bu sadece sizin fikriniz mi yoksa bir şekilde açıklayabilir misiniz?
tatarcık

Bu seçenek tam olarak nasıl desteklenmiyor, her seçeneğin açıkladığı gibi (gerçekten çok fazla kelime olmadan) neden kullanılıyor?
Nikos M.

bunu söylemek istemem, ama (benim) 'deki olumsuz ifadeler bağlam dışıdır :). OP sorulan soru, alternatif cevabınız olup olmadığı tamamen bir başka meseledir
Nikos M.

Gerçekten açıklamayı burada göremiyorum. Diyelim ki, birisi "kritik bölüm ve varsayılanlar yok => Erken Başarısız Olma" gibi başka bir görüş yazarsa , bu cevap okuyucunun karşıt olan iki görüşü seçmesine nasıl yardımcı olur? Düşünün düzenlemek uyacak şekilde, daha iyi bir şekle ing Yanıt nasıl yönergelere.
tatarcık

Tamam, görüyorum ki, bu gerçekten başka bir açıklama olabilir, ama en azından "kritik bölüm" kelimesini anlıyorsunuz ve "temerrütsüz" kelimesini anlıyorsunuz; bu, erken başarısızlığa yol açacak bir strateji anlamına gelebilir ve bu gerçekten de minimalist olsa da bir genişlemedir
Nikos M.
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.