Başarısız olduğunda başarılı ve istisna atarken, true / false vs.


22

Bir dosya yükleyen bir işlev olan bir API yapıyorum. Bu işlev, dosya doğru yüklendiyse hiçbir şey döndürmez / geçersiz kılar ve bir sorun olduğunda bir istisna atar.

Neden bir istisna ve sadece yanlış değil? Çünkü bir istisna içerisinde başarısızlık sebebini belirleyebilirim (bağlantı yok, eksik dosya adı, yanlış şifre, eksik dosya açıklaması vb.). Özel bir istisna oluşturmak istedim (API kullanıcısının tüm hataları işlemesine yardımcı olmak için bazı numaralandırmalarla).

Bu iyi bir uygulama mı yoksa bir nesneyi (daha iyi bir şekilde, isteğe bağlı bir hata mesajı ve hatalar için numaralandırma ile) döndürmek daha mı iyi?



59
Doğru ve yanlış birlikte iyi gider. Boşluk ve istisna birlikte iyi gider. Gerçek ve istisna birlikte kediler ve vergiler gibi gider. Bir gün kodunu okuyor olabilirim. Lütfen bunu bana yapma.
candied_orange 12:16

4
Başarı veya başarısızlığı içine alan bir nesnenin döndürüldüğü kalıpları oldukça seviyorum. Örneğin, Rust başarılı olursa sonucun Result<T, E>nerede Tolduğunu ve Ebaşarısız olursa hatadır. Bir istisna atmak (Java'da emin olamaz), çağrı yığınını çözmeyi gerektirdiğinden pahalı olabilir, ancak bir Exception nesnesi oluşturmak ucuzdur. Açıkçası, kullandığınız dilin yerleşik kalıplarına sadık kalın, ancak bunun gibi booleyleri ve istisnaları birleştirmeyin.
Dan Pantry

4
Burada dikkatli ol. Bir tavşan deliğinden aşağıya doğru yönelebilirsiniz. Programınız bir sorunla başa çıkabiliyorsa, ön koşulları kontrol ederek sorunu tespit etmek ve çözmek genellikle daha kolaydır. Bir istisnayı nadiren yakalar, verilerini kodda inceler ve sorunu düzelttikten sonra tekrar deneyin. Bu nedenle, pek çok istisna türüne sahip olmak nadiren değerlidir. Sorunları günlüğe kaydetmeye çalışıyorsanız , bu çok daha mantıklıdır, ancak önemli miktarda kod içeren bir sürü catch bloğu yazmayı beklemeyin.
jpmc26 12:16

4
@DanPantry Java'da, istisna oluştururken çağrı yığını, atma sırasında değil kontrol edilir . fillInStackTrace();sınıfta süper yapıcı, denirThrowable
Simon Forsberg

Yanıtlar:


35

Bir istisna atmak, bir yöntemin bir değer getirmesini sağlamanın ek bir yoludur. Arayan bir istisna yakalamak kadar kolay bir dönüş değeri kontrol edebilir ve kontrol edebilir. Bu nedenle, diğer kriterler arasında karar vermek throwve bunu returngerektirir.

Programınızın verimliliğini tehlikeye atıyorsa, istisnalardan kaçınılmalıdır (bir istisna nesnesi oluşturmak ve çağrı yığınını açmak bilgisayar için sadece bir değere itmekten çok daha fazla iş demektir). Ancak, yönteminizin amacı bir dosya yüklemekse, tıkanıklık her zaman ağ ve dosya sistemi G / Ç olacaktır, bu nedenle geri dönüş yöntemini optimize etmek anlamsızdır.

API kullanıcılarının beklentilerini ihlal ettiği için basit bir kontrol akışı olması gerekenler için istisnalar atmak da kötü bir fikirdir (örneğin, bir arama değerini bulmakta başarılı olur). Ama amacını yerine getirmek için başarısız bir yöntem olduğunu bir istisna atma için hiçbir neden görmüyorum bu yüzden, (ya da en azından olması gerektiği) olağanüstü bir durum. Ve bunu yaparsanız, onu daha özel, daha bilgilendirici bir istisna haline getirebilirsiniz (ancak, bunun gibi standart, daha genel bir istisna alt sınıfı haline getirmek iyi bir fikirdir IOException).


7
Bir dosya yükleme isteğinin başarısız olması beklenen bir sonuçsa, yüzüme atılan bir istisna karşısında şaşırdım (özellikle de yöntem imzasında bir iade değeri varsa).
Hoffmale

1
Standart bir istisna oluşturmanın sebebinin, birçok (muhtemelen en fazla) arayan kişinin sorunu gidermeye çalışmak için kod yazmayacağına dikkat edin. Sonuç olarak, çoğu arayan basit bir şekilde "bu büyük istisnalar grubunu yakalamak" isteyecektir.
jpmc26 12:16

4
@gbjbaanb Bu nedenle, bazı düzeltilemeyen bir durum olduğunda istisnalar kullanılmalıdır. Bir dosyayı açmaya çalışıyorsanız ve dosya okunamıyorsa, bu bir istisna için iyi bir durumdur. Dosyanın varlığını kontrol ettiğinizde, bir booleanın kullanılması gerekir çünkü dosyanın orada bulunmaması beklenir.
Malcolm

21
Kilian, özyinelemeli sloganda "istisnalar istisnai durumlar içindir" derken, istisnai durumların fonksiyonun amacını yerine getiremediği durumlar olduğunu söylüyor. İşlevin bir dosyayı okuması gerekiyorsa ve dosya okunamıyorsa , bu istisnai bir durumdur . Eğer işlevin size bir dosyanın olup olmadığını söylemesi gerekiyorsa (potansiyel TOCTOU’yu dikkate almayın), o zaman mevcut olmayan dosya istisnai değildir çünkü işlev hala yapması gerekeni yapabilir. İşlevin bir dosyanın var olduğunu iddia etmesi gerekiyorsa, o zaman istisnai değildir çünkü "assert" ifadesi budur.
Steve Jessop

10
Bir dosyanın var olup olmadığını belirten bir işlev ile bir dosyanın var olduğunu iddia eden bir işlev arasındaki tek farkın, dosyanın bulunmadığı durumun istisnai olup olmadığını unutmayın. İnsanlar bazen "istisnai" olsun veya olmasın hata durumunun bir özelliği gibi konuşurlar. Hata durumunun bir özelliği değildir, hata durumunun birleşik bir özelliğidir ve gerçekleştiği işlevin amacıdır. Aksi takdirde, istisnaları asla yakalayamazdık.
Steve Jessop

31

trueBaşarısızlık yapmazsanız başarıya geri dönmek için kesinlikle hiçbir neden yoktur false. Müşteri kodu neye benzemeli?

if (result = tryMyAPICall()) {
    // business logic
}
else {
    // this will *never* happen anyways
}

Bu durumda, arayan kişi zaten bir try-catch bloğuna ihtiyaç duyar, ancak daha sonra daha iyi yazabilir:

try {
    result = tryMyAPICall();
    // business logic
    // will only reach this line when no exception
    // no reason to use an if-condition
} catch (SomeException se) { }

Bu yüzden truegeri dönüş değeri arayan için tamamen önemli değil. Öyleyse sadece yöntemi koru void.


Genel olarak, arıza modları tasarlamak için üç yol vardır.

  1. Doğru / yanlış döndür
  2. Kullan void, at (işaretli) istisna
  3. ara sonuç nesnesini döndür.

İade true/false

Bu, bazı eski, çoğunlukla c tarzı API'lerde kullanılır. Dezavantajları obviuos, neyin yanlış gittiğini bilmiyorsun. PHP bunu oldukça sık yapar ve bu şekilde bir koda yol açar:

if (xyz_parse($data) === FALSE)
   $error = xyz_last_error();

Çok iş parçacıklı bağlamlarda, bu daha da kötüdür.

Atma (işaretli) istisnaları

Bu yapmak için güzel bir yol. Bazı noktalarda başarısızlık bekleyebilirsiniz. Java bunu soketlerle yapar. Temel varsayım, bir çağrının başarılı olması gerektiği, ancak herkes bazı işlemlerin başarısız olabileceğini biliyor. Soket bağlantıları aralarında. Böylece arayan başarısızlıkla başa çıkmak zorunda kalır. güzel bir tasarım, çünkü arayan kişinin arızayı çözdüğünden emin olur ve arayan kişiye başarısızlıkla başa çıkabilmesi için zarif bir yol sunar.

Sonuç nesnesini döndür

Bu, bunun üstesinden gelmenin başka bir güzel yoludur. Genellikle ayrıştırma işlemi veya onaylanması gereken şeyler için kullanılır.

ValidationResult result = parser.validate(data);
if (result.isValid())
    // business logic
else
    error = result.getvalidationError();

Arayan için de güzel, temiz bir mantık.

İkinci vakanın ne zaman ve üçüncüün ne zaman kullanılacağı konusunda biraz tartışma vardır. Bazı insanlar istisnaların istisnai olması gerektiğine ve istisnalar olasılığını göz önünde bulundurarak tasarım yapmamanız gerektiğine inanmaktadır ve hemen hemen her zaman üçüncü seçenekleri kullanacaklardır. Bu güzel. Ama Java istisnalar kontrol, bu yüzden hiç bir neden görmüyorum değil onları kullanmak. Temel varsayım, aramanın başarılı olması gerektiği (bir soket kullanmak gibi) olduğu, ancak başarısızlığın mümkün olduğu ve denetlenmeyen ifadeleri kullanıyorum ve aramanın başarılı olup olmadığının (verilerin doğrulanması gibi) belirsiz olduğu durumlarda üçüncü seçeneği kullanıyorum. Ancak bu konuda farklı görüşler var.

Senin durumunda void+ ile giderdim Exception. Dosya yüklemesinin başarılı olmasını ve olmadığında istisnai olmasını beklersiniz. Ancak arayan kişi bu arıza modunu kullanmaya zorlanır ve ne tür bir hatanın meydana geldiğini doğru şekilde açıklayan bir İstisna gönderebilirsiniz.


5

Bu gerçekten bir hatanın istisnai ya da beklenen olup olmadığına bağlıdır .

Bir hata genellikle beklediğiniz bir şey değilse, API kullanıcısının özel bir hata işleme koymadan yalnızca yönteminizi çağırması muhtemeldir. Bir istisna atmak, yığının fark edileceği bir yere kabarcıklanmasını sağlar.

Öte yandan, hatalar olağansa, onları kontrol eden geliştirici için en iyi duruma getirmelisiniz ve try-catchyan tümceleri bir if/elifveya switchseriden biraz daha hantaldır .

Daima, kullanan bir kişinin zihniyetinde bir API tasarlayın.


1
Benim durumumda, birinin dediği gibi, API kullanıcısının dosyayı yükleyememesi olağanüstü bir hata olmalı.
Accollativo 13:16

4

Asla geri dönmeyi düşünmüyorsan bir boolean döndürme false. Sadece yöntem voidve belgeyi IOExceptionbaşarısızlığa karşı bir istisna atacağını ( uygun) yapmasını sağlayın .

Bunun nedeni, bir boolean döndürürseniz, API'nizin kullanıcısının bunu yapabileceği sonucuna varmış olabilir:

if (fileUpload(file) == false) {
    // Handle failure.
}

Bu elbette işe yaramayacak; yani, yönteminizin sözleşmesi ile davranış şekli arasında bir uyumsuzluk var. İşaretli bir istisna atarsanız, yöntemin kullanıcısı arızayı ele almak zorundadır:

try {
    fileUpload(file);
} catch (IOException e) {
    // Handle failure.
}

3

Metodunuzun bir geri dönüş değeri varsa, bir istisna atmak kullanıcılarını şaşırtabilir. Bir yöntemin bir boolean döndürdüğünü görürsem, başarılı olursa yanlış dönerse doğru döneceği ve eğer if-else cümlesi kullanarak kodumu yapılandıracağım konusunda oldukça ikna oldum. Özel durumunuz yine de bir numaralandırmaya göre yapılacaksa, Windows HResults'a benzer şekilde bunun yerine bir numaralama değeri de verebilir ve yöntemin başarılı olduğu zamanki bir numaralama değerini saklayabilirsiniz.

Aynı zamanda, bir prosedürdeki son adım olmadığı zaman, bir metot atma istisnasına sahip olmak can sıkıcıdır. Bir toplama-yakalama kontrol akışının tutma bloğuna yazılması, spagetti için iyi bir reçetedir ve bundan kaçınılmalıdır.

İstisnalarla ileriye gidiyorsanız, bunun yerine boşluğu döndürmeyi deneyin, kullanıcılar istisnalar atılmadığında başarılı olduklarını anlarlar ve hiçbiri kontrol akışını yönlendirmek için if-else cümlesi kullanmaya çalışır.


1
Enum, API kullanıcısının hata iletisini ayrıştırmadan farklı türdeki hataları işlemesine yardımcı olması için bir fikirdi. Yani benim açımdan bakış açımdan enum döndürmek ve "Tamam" için bir enum eklemek daha iyidir.
Accollativo
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.