İstisnaların yalnızca istisnai durumlarda kullanılması gerektiği söylendi. Davamın istisnai olup olmadığını nasıl bilebilirim?


99

Buradaki özel durum, kullanıcının bir dizgeyi uygulamaya geçirebilmesidir, uygulama onu ayrıştırır ve yapılandırılmış nesnelere atar. Bazen kullanıcı geçersiz bir şey yazabilir. Örneğin, girişleri bir kişiyi tanımlayabilir, ancak yaşlarının “elma” olduğunu söyleyebilirler. Bu durumda doğru davranış işlemi geri alır ve kullanıcıya bir hata olduğunu bildirir ve tekrar denemeleri gerekir. Girişte bulabildiğimiz her hatayı, yalnızca birinciyi değil, rapor etmek gerekebilir.

Bu durumda, bir istisna atmamız gerektiğini savundum. "İstisnalar olağanüstü olmalı: Kullanıcının geçersiz veriler girmesi bekleniyor, bu nedenle bu istisnai bir durum değil" diyerek aynı fikirde değildi, çünkü kelimenin tam anlamıyla nasıl konuşacağını bilmiyordum. doğru görünüyor.

Fakat benim anladığım kadarıyla istisnaların ilk başta icat edilmesinin nedeni bu. Bu eskiden yoktu bir hata oluştu görmek için sonuç incelemek için. Eğer kontrol etmediyseniz, farketmeden kötü şeyler olabilir.

İstisnalar dışında, yığının her seviyesinin çağırdıkları yöntemlerin sonucunu kontrol etmesi gerekir ve bir programcı bu seviyelerden birini kontrol etmeyi unutursa, kod yanlışlıkla ilerleyebilir ve geçersiz verileri kaydedebilir (örneğin). Daha fazla hata bu şekilde eğilimli görünüyor.

Her neyse, burada söylediğim her şeyi düzeltmekte özgürsün. Asıl sorum, birinin İstisnalar'ın istisnai olması gerektiğini söylemesi durumunda, davamın istisnai olup olmadığını nasıl bilebilirim?


3
olası yinelenen? Bir istisna ne zaman atılmalıdır . Orada kapalı olmasına rağmen, sanırım buraya uyuyor. Hala biraz felsefe, bazı insanlar ve topluluklar istisnaları bir tür akış kontrolü olarak görme eğilimindedir.
thorsten müller

8
Kullanıcılar aptal olduğunda, geçersiz girdi sağlarlar. Kullanıcılar akıllı olduklarında geçersiz giriş sağlayarak oynarlar. Bu nedenle, geçersiz kullanıcı girişi bir istisna değildir.
mouviciel

7
Ayrıca, Java ve .NET'te çok özel bir mekanizma olan bir istisnayı çok daha genel bir terim olan bir hatayla karıştırmayın . Fazlası var işlenmesini hata durumlar atma daha. Bu tartışma istisnalar ve hatalar arasındaki farklara değiniyor .
Eric King,

4
"İstisnai"! = "Nadiren Olur"
ConditionRacer

3
Eric Lippert'in Vexing istisnalarının iyi bir tavsiye olduğunu düşünüyorum.
Brian

Yanıtlar:


87

Daha az kod karmaşasıyla hata işlemeyi kolaylaştırmak için istisnalar icat edildi. Daha az kod karmaşası ile hata işlemeyi kolaylaştırdıkları durumlarda bunları kullanmalısınız. Bu "yalnızca istisnai durumlar için istisnalar" işletmesi, istisna işleminin kabul edilemez bir performansa neden olduğu bir zamandan kaynaklanmaktadır. Artık kodun büyük çoğunluğunda durum böyle değil, ancak insanlar bunun arkasındaki nedeni hatırlamadan hâlâ kural koyuyorlar.

Özellikle belki de şimdiye kadar tasarlanan en istisnai sevgi dolu dil olan Java'da, kodunuzu basitleştirirken istisnaları kullanma konusunda kendinizi kötü hissetmemelisiniz . Aslında, Java'nın kendi Integersınıfının, bir dizgenin potansiyel olarak bir atama yapmadan geçerli bir tamsayı olup olmadığını denetleme yolu yoktur NumberFormatException.

Ayrıca, yalnızca kullanıcı arayüzü doğrulamasına güvenemeseniz de , kullanıcı arayüzünüzün kısa sayısal değerler girmek için bir eğirici kullanmak gibi uygun bir şekilde tasarlanmış olup olmadığını, ardından arka uca dönüştüren sayısal olmayan bir değerin gerçekten iyi olacağını unutmayın. istisnai durum.


10
Güzel tokatlamak, orada. Aslında tasarlanmış gerçek dünya uygulamasında, performans isabet yaptığı bir fark yaratmak ve ben bunu değiştirmek zorunda değil belli ayrıştırma işlemleri için istisna atar.
Robert Harvey

17
Performans isabetinin geçerli bir sebep olduğu durumlarda hala bir durum olmadığını söylemiyorum, ancak bu durumlar kuraldan ziyade istisnadır (amaçlanan).
Karl Bielefeldt

11
@RobertHarvey Java’daki hile yerine önceden hazırlanmış istisna nesneler atmak değil throw new .... Veya fillInStackTrace () öğesinin üzerine yazıldığı özel istisnaları atın. O zaman isabetten bahsetmek yerine performansın bozulmadığını fark etmemeniz gerekir .
Ingo

3
+1: Kesinlikle cevaplayacağım şeydi. Kodu basitleştirdiğinde kullanın. İstisnalar, çağrı yığınındaki her seviyede geri dönüş değerlerini kontrol etmekte zorlanmadığınız durumlarda daha net bir kod verebilir. (Yine de her şey gibi, yanlış bir şekilde kullanılırsa kodunuzu korkunç bir karışıklık yaratabilir.)
Leo

4
@Brendan Bazı istisnaların ortaya çıktığını ve hata işleme kodunun çağrı yığında 4 seviyenin altında olduğunu varsayalım. Bir hata kodu kullanırsanız, işleyicinin üzerindeki 4 işlevin de, hata kodunun türünü, dönüş değeri olarak alması gerekir if (foo() == ERROR) { return ERROR; } else { // continue }ve her seviyede bir zincir yapmalısınız . Denetlenmeyen bir istisna atarsanız, "hata döndürme hatası" olduğunda gürültülü ve fazlalık olmaz. Ayrıca, işlevleri argüman olarak iletiyorsanız, bir hata kodu kullanmak, hata oluşmasa bile işlev imzasını uyumsuz bir tür olarak değiştirebilir.
Doval

72

Bir istisna ne zaman atılmalıdır? Kod gelince, aşağıdaki açıklamanın çok faydalı olduğunu düşünüyorum:

Bunun bir istisnası, bir üye görevi tamamlayamadığında, adında belirtildiği şekilde gerçekleştirmesi beklenir . (Jeffry Richter, C # ile CLR)

Neden yardımcı oluyor? Bir şeyin istisna olarak ne zaman ele alınması gerektiği bağlamına bağlı olduğunu göstermektedir. Yöntem çağrıları düzeyinde, bağlam (a), (b) yöntemin imzası ve (b) yöntemi kullanan veya kullanması beklenen müşteri kodu ile verilir.

Sorunuzu cevaplamak için kullanıcı girişinin işlendiği koda bir göz atmanız gerekir. Bu gibi bir şey görünebilir:

public void Save(PersonData personData) {  }

Yöntem adı bazı doğrulamaların yapıldığını gösteriyor mu? Hayır. Bu durumda geçersiz bir PersonData bir istisna atmalıdır.

Sınıfın buna benzeyen başka bir yöntemi olduğunu varsayalım:

public ValidationResult Validate(PersonData personData) {  }

Yöntem adı bazı doğrulamaların yapıldığını gösteriyor mu? Evet. Bu durumda geçersiz bir PersonData bir istisna atmamalıdır.

Her şeyi bir araya getirmek için, her iki yöntem de müşteri kodunun şöyle görünmesi gerektiğini önermektedir:

ValidationResult validationResult = personRegister.Validate(personData);
if (validationResult.IsValid())
{
    personRegister.Save(personData)
}
else
{
    // Throw an exception? To answer this look at the context!
    // That is: (a) Method name, (b) signature and
    // (c) where this method is (expected) to be used.
}

Bir yöntemin bir istisna atması gerekip gerekmediği açık değilse, o zaman belki kötü seçilmiş bir yöntem adı veya imzası olabilir. Belki sınıfın tasarımı açık değildir. Bir istisna atılıp atılmaması gereken soruya net bir cevap almak için bazen kod tasarımını değiştirmeniz gerekir.


Daha dün bir yapılan structaçıkladığınız kodum "ValidationResult" denir ve yapılandırılmış bir yol.
Paul

4
Sorunuzu yanıtlamamıza yardımcı olmuyor, ancak Komut-sorgu ayrıştırma ilkesini ( en.wikipedia.org/wiki/Command-query_separation ) örtük veya kasten uyguladığınızı belirtmek isterim . ;-)
Theo Lenndorff

İyi fikir! Tek dezavantajı: Örneğinizde doğrulama aslında iki kez yapılır: Bir kez Validate(geçersizse False döndürerek) ve bir kez Save(( geçersizse belirli, iyi belgelenmiş bir istisna) atma). Elbette, doğrulama sonucu nesnenin içine önbelleklenebilir, ancak doğrulama sonucunun değişikliklerde geçersiz kılınması gerektiğinden, bu ek bir karmaşıklık ekler.
Heinzi,

@Heinzi katılıyorum. Yöntemin Validate()içinde adlandırılacak şekilde yeniden yapılandırılabilir ve istisna için uygun bir mesaj oluşturmak için kullanılabilecek Save()özel detaylar ValidationResultkullanılabilir.
Phil

3
Bu sanırım kabul edilen cevaptan daha iyi. Çağrı yapması gereken şeyi yapamadığı zaman atın.
Andy

31

İstisnalar istisnai olmalıdır: Kullanıcının geçersiz veri girmesi beklenir, bu istisnai bir durum değildir.

Bu argümanda:

  • Bir dosyanın bulunmaması beklenir, bu nedenle istisnai bir durum değildir.
  • Sunucuyla olan bağlantının kesilmesi beklenebilir, bu durum istisnai bir durum değildir.
  • İstisnai bir durum olmaması için konfigürasyon dosyasının bozuk olması beklenir
  • İsteğinizin bazen düşebileceği beklenir, bu nedenle bu istisnai bir durum değildir.

Yakaladığınız herhangi bir istisna, beklemelisiniz, çünkü onu yakalamaya karar verdiniz. Ve bu nedenle bu mantıkla, asla yakalamayı düşündüğünüz herhangi bir istisnayı atmamalısınız.

Bu nedenle, "istisnalar olağanüstü olmalı" nın korkunç bir kural olduğunu düşünüyorum.

Yapmanız gereken, dile bağlıdır. Farklı dillerin, istisnaların ne zaman atılması gerektiğiyle ilgili farklı kuralları vardır. Python, örneğin, her şey için istisnalar atar ve Python'da, ben de uyumu izlerim. Öte yandan, C ++, göreceli olarak birkaç istisna atar ve ben de bunu izlerim. C ++ veya Java'yı Python gibi ele alabilir ve her şey için istisnalar atabilirsiniz, ancak dilin kendisinin nasıl kullanılmasını beklediğine bağlı olarak çalışabilirsiniz.

Python'un yaklaşımını tercih ediyorum ama bence diğer dilleri içine atmak kötü bir fikir.


1
@gnat, biliyorum. Demek istediğim, sizin favori olmasanız bile, dilin kurallarını (bu durumda Java) izlemeniz gerektiği idi.
Winston Ewert,

6
+1 "exceptions should be exceptional" is a terrible rule of thumb.Peki dedi! Bu, insanların düşünmeden tekrar ettiği şeylerden biri.
Andres F.

2
“Beklenen”, öznel argüman veya kongre tarafından değil, API / işlevin sözleşmesiyle tanımlanır (bu açık olabilir, ancak çoğu zaman sadece ima edilir). Farklı işlevler / API'ler / alt sistemler farklı beklentilere sahip olabilir, örneğin bazı üst seviye işlevsellikler için mevcut olmayan bir dosya işlenmesi için beklenen bir durumdur (bunu bir GUI aracılığıyla bir kullanıcıya rapor edebilir), diğer alt düzey işlevler için ise Muhtemelen değil (ve bu nedenle bir istisna atmak gerekir). Bu cevap önemli noktayı .... kaçırmak gibi görünüyor
Mikera

1
@mikera, evet, bir işlev (yalnızca) sözleşmesinde belirtilen istisnaları atmalıdır. Ama soru bu değil. Sorun, bu sözleşmenin ne olması gerektiğine nasıl karar verdiğinizdir. Bu kuralın uygulanmasında “istisnalar istisnai olmalı” kuralının kullanılmadığına inanıyorum.
Winston Ewert,

1
@supercat, bunun daha yaygın olmasıyla sonuçlandığını gerçekten sanmıyorum. Bence kritik soru mantıklı bir temele sahip olmak. Açıkça hata koşulunu yerine getirmezsem kodum hiçbir şey olmamış gibi mi davranıyor veya yararlı bir hata mesajı mı alıyorum?
Winston Ewert,

30

İstisnaları düşünürken her zaman veritabanı sunucusuna veya web API'sine erişmek gibi şeyleri düşünüyorum. Sunucu / web API'sinin çalışmasını beklersiniz, ancak istisnai bir durumda çalışmayabilir (sunucu kapalı). Bir web isteği genellikle hızlı olabilir, ancak istisnai durumlarda (yüksek yük) zaman aşımına uğrayabilir. Bu senin kontrolün dışında bir şey.

Kullanıcıların girdi verileri sizin kontrolünüzdedir, çünkü ne gönderdiklerini kontrol edebilir ve onunla ne isterseniz yapabilirsiniz. Senin durumunda, kaydetmeyi denemeden önce kullanıcı girişini doğrulardım. Ve geçersiz veri sağlayan kullanıcıların beklenmesinin beklendiği ve uygulamanızın girişi doğrulayarak ve kullanıcı dostu hata mesajı sağlayarak bunu hesaba katması gerektiği konusunda hemfikiriz.

Bununla birlikte, etki alanı modeli belirleyicilerimin çoğunda istisnalar kullanıyorum, geçersiz verilerin girme ihtimalinin kesinlikle olmaması gerekiyor. Ancak, bu son bir savunma hattı ve giriş formlarımı zengin doğrulama kuralları ile oluşturma eğilimindeyim. , bu yüzden bu etki alanı modeli istisnasını tetikleme şansı neredeyse yoktur. Yani bir pasör bir şeyi beklerken ve bir tane daha elde ederse, sıradan koşullarda gerçekleşmemesi gereken istisnai bir durumdur.

EDIT (dikkate alınması gereken başka bir şey):

Db'ye kullanıcı tarafından sağlanan verileri gönderirken, önceden tabloları neye girmeniz ve neyin girmemesi gerektiğini bilirsiniz. Bu, verilerin beklenen bir formata göre doğrulanabileceği anlamına gelir. Bu kontrol edebileceğiniz bir şey. Kontrol edemediğiniz şey, sunucunuzun sorgunuzun ortasında kalmamasıdır. Yani sorgunun iyi olduğunu ve verilerin filtrelendiğini / doğrulandığını biliyorsunuz, sorguyu deniyorsunuz ve hala başarısız oluyor, bu istisnai bir durum.

Web isteklerine benzer şekilde, isteğin zaman aşımına uğrayacağını veya göndermeyi denemeden önce bağlanmanın başarısız olup olmadığını bilemezsiniz. Bu da bir deneme / yakalama yaklaşımı garanti ediyor, çünkü sunucuya isteği gönderdikten sonra birkaç milisaniyede çalışıp çalışmayacağını soramazsınız.


8
Ama neden? İstisnalar neden daha fazla beklenen problemlerin ele alınmasında daha az faydalıdır?
Winston Ewert

6
@Pinetree, bir dosyayı açmadan önce dosyanın varlığını kontrol etmek kötü bir fikirdir. Dosya, çek ile açık arasında bulunmayı durdurabilir, dosyayı açmanıza izin veren izniniz olamazdı ve var olup olmadığını kontrol edin ve ardından dosyayı açmak iki pahalı sistem çağrısı gerektirir. Dosyayı açmaya çalışırken daha sonra başarısız olmayla başa çıkmanız daha iyi olacaktır.
Winston Ewert,

10
Görebildiğim kadarıyla, muhtemel tüm başarısızlıklar, başarısızlıktan kurtulmak yerine, daha önce elde başarıyı kontrol etmeye çalışmak yerine daha iyi ele alınmaktadır. İstisnalar veya başka bir şey kullanıp kullanmamanız başarısızlığa işaret ediyor ise ayrı bir konudur. İstisnaları tercih ederim çünkü kazayla onları görmezden gelemem.
Winston Ewert,

11
Geçersiz kullanıcı verilerinin beklendiği için istisnai olarak kabul edilemeyeceği fikrine katılmıyorum. Bir ayrıştırıcı yazarsam ve birileri bu veriyi çözümlenemez bir veri beslerse, bu bir istisnadır. Ayrıştırmaya devam edemiyorum. İstisnaların nasıl ele alındığı tamamen başka bir sorudur.
ConditionRacer

4
File.ReadAllBytes FileNotFoundException, yanlış girdi verildiğinde bir zaman atacaktır (örneğin olmayan bir dosya adı). Bu hatayı tehdit etmenin tek geçerli yolu budur, hata kodlarını döndürmeden başka ne yapabilirsiniz?
o

16

Referans

Gönderen Pragmatik Programcı:

İstisnaların, programın normal akışının bir parçası olarak nadiren kullanılması gerektiğine inanıyoruz; Beklenmeyen olaylar için istisnalar ayrılmalıdır. Yakalanmamış bir istisnanın programınızı sonlandıracağını ve kendinize "Tüm istisna işleyicileri kaldırırsam bu kod hala çalışır mı?" Cevabınız "hayır" ise, istisnalar istisnai olmayan durumlarda kullanılıyor olabilir.

Okuma için bir dosya açma örneğini incelemeye devam ediyorlar ve dosya mevcut değil - bu bir istisna yaratmalı mı?

Dosya halinde olmalıdır olmuştur, daha sonra bir istisna garanti edilir. [...] Öte yandan, dosyanın var olup olmaması hakkında hiçbir fikriniz yoksa, bulamıyorsanız olağanüstü görünmüyor ve bir hata dönüşü uygun.

Daha sonra neden bu yaklaşımı seçtiklerini tartışıyorlar:

[A] n istisnası, derhal, yerel olmayan bir kontrol transferini temsil eder - bu bir çeşit basamaklandırmadır goto. Özel işlemlerini normal işlemlerinin bir parçası olarak kullanan programlar, klasik spagetti kodunun tüm okunabilirliği ve bakımıyla ilgili sorunlardan muzdariptir. Bu programlar enkapsülasyonu bozar: rutinler ve arayanlar istisna yönetimi ile daha sıkı bir şekilde birleştirilir.

Durumunuzla ilgili olarak

Sorunuz "Doğrulama hataları istisnalar doğurmalı mı?" Cevap, doğrulamanın nerede gerçekleştiğine bağlı olmasıdır.

Söz konusu yöntem, girdi verilerinin zaten doğrulanmış olduğu varsayılan kodun bir bölümünde ise, geçersiz girdi verileri bir istisna oluşturmalıdır; Kod, bu yöntemin bir kullanıcı tarafından girilen tam girişi alacak şekilde tasarlanmışsa, geçersiz veriler beklenir ve bir istisna oluşturulmamalıdır.


11

Burada çok fazla felsefi kimlik belirtisi var, ancak genel olarak konuşmak gerekirse, istisnai koşullar basitçe kullanıcı müdahalesi olmadan ele almak istemediğiniz veya kullanamayacağınız (temizleme, hata raporlama ve benzerleri hariç) durumlardır. Başka bir deyişle, onlar kurtarılamaz koşullardır.

Bir programa bir dosya yolu yazarsanız, o dosyayı bir şekilde işleme koymak niyetindeyseniz ve bu yolla belirtilen dosya mevcut değilse, bu istisnai bir durumdur. Bununla ilgili kodunuzda, kullanıcıya rapor vermekten başka farklı bir dosya yolu belirlemelerine izin vermekten başka bir şey yapamazsınız.


1
+1, söyleyeceğim şeye çok yakın. Kapsam hakkında daha fazla olduğunu ve gerçekten kullanıcı ile ilgisi olmadığını söyleyebilirim. Buna iyi bir örnek, iki Net fonksiyonları int.Parse ve int.TryParse arasındaki farktır, eski, daha sonra bir istisna atmak asla kötü girişe bir istisna başka çaresi vardır
jmoreno

1
@jmoreno: Ergo, kodun kabul edilemez durum hakkında bir şeyler yapması için TryParse ve kullanamadığı zaman Ayrıştır.
Robert Harvey,

7

Göz önünde bulundurmanız gereken iki endişe var:

  1. Tek bir endişeyi tartışıyorsunuz - Assignerbu endişeyi yapılandırılmış nesnelere girdiler atamak olduğu için çağıralım - ve girdilerinin geçerli olacağı kısıtını ifade edersiniz.

  2. Bir iyi uygulanmış kullanıcı arayüzü başka bir endişeniz var hatalarında kullanıcı girişi ve yapıcı geribildirim doğrulama (en bu bölümü diyebiliriz Validator)

AssignerBileşenin bakış açısından, bir istisna atılması tamamen makul, çünkü ihlal edilmiş bir kısıtlamayı dile getirdiniz.

Bakış açısından kullanıcı deneyimi , kullanıcı bu doğrudan konuşarak olmamalıdır Assignerilk etapta. Onlar konuşmam gereken yoluylaValidator .

Şimdi, Validatorgeçersiz kullanıcı girişi, istisnai bir durum değildir , gerçekten ilgilendiğiniz durumdur. Dolayısıyla, burada bir istisna uygun olmaz ve bu, aynı zamanda tüm hataları tanımlamak istediğinizde de geçerlidir. ilkinden kurtulma.

Bu endişelerin nasıl uygulandığından bahsetmediğimi fark edeceksiniz . Görünüşe göre Assigneriş arkadaşınız bir araya gelerek konuşuyor Validator+Assigner. Eğer fark Oraya vardır en azından makul tartışabilirsiniz, iki ayrı (veya ayrılabilir) endişeler.


Renan'ın yorumunu gidermek için, sadece ediyorum varsayarak size iki ayrı endişeleri belirledikten sonra, her bağlamda istisnai düşünülmelidir hangi durumlarda bariz olduğunu.

Aslında, bir şeyin istisnai olarak kabul edilip edilmeyeceği açık değilse , muhtemelen çözümünüzdeki bağımsız endişeleri belirlemeyi tamamlamadığınızı iddia ediyorum.

Bu doğrudan cevap yapar sanırım

... davamın istisnai olup olmadığını nasıl anlarım?

açık olana kadar basitleştirmeye devam edin . İyi anladığınız basit kavramlar yığını olduğunda, bunları tekrar koda, sınıflara, kütüphanelere veya her neyse yeniden oluşturma konusunda net bir şekilde karar verebilirsiniz.


-1 Evet, iki kaygı var ama bu "Davamın istisnai olup olmadığını nasıl bilebilirim?" Sorusuna cevap vermiyor.
RMalke

Mesele aynıdır, başka bir durumda değil, bir bağlamda istisnai olabilir. Hangi bağlamda konuştuğunuzu belirlemek (ikisini birleştirmek yerine) burada soruyu yanıtlar.
İşe yaramaz

... aslında, belki de değil - cevabımdaki amacına değindim.
İşe yaramaz

4

Diğerleri de iyi cevap verdiler ama yine de kısa cevap. İstisna, ortamdaki bir şeyin yanlış olduğu, kontrol edemediğiniz ve kodunuzun ileri gidemediği bir durumdur. Bu durumda, kullanıcıya neyin yanlış gittiğini, neden daha ileri gidemeyeceğinizi ve çözümün ne olduğunu bildirmeniz gerekir.


3

Asla, sadece istisnai durumlarda istisnalar atmanız gerektiği konusunda tavsiyenin büyük bir hayranı olmadım, kısmen bir şey söylemediği için (sadece yenilebilir yiyecekleri yemelisiniz demek gibi), aynı zamanda çok özneldir ve genellikle istisnai bir durumu neyin oluşturduğu ve neyin yaratmadığı açık değildir.

Bununla birlikte, bu tavsiyenin iyi nedenleri vardır: istisnalar atma ve yakalama yavaştır ve kodunuzu Visual Studio'daki hata ayıklayıcısında çalıştırıyorsanız, bir istisna atıldığında sizi uyarmak üzere ayarlanmışsa, onlarca spam tarafından karşılanabilirsiniz eğer yüzlerce mesaj olmasaydı soruna daha önce ulaşabilirsin.

Genel bir kural olarak, eğer:

  • kodunuz hatasız ve
  • bağlı olduğu servislerin tümü kullanılabilir durumdadır ve
  • Kullanıcınız, programınızı, kullanılması amaçlandığı şekilde kullanıyor (sağladıkları girdilerin bazıları geçersiz olsa bile)

o zaman kodunuz asla bir istisna atmamalıdır, daha sonra yakalananlar bile. Geçersiz verileri yakalamak için, kullanıcı arayüzündeki doğrulayıcıları veya Int32.TryParse()sunum katmanındaki gibi kodları kullanabilirsiniz .

Başka bir şey için, bir istisnanın, yönteminizin adının söylediklerini yapamayacağı anlamına geldiği ilkesine bağlı kalmalısınız . Genel olarak başarısızlığı belirtmek için dönüş kodları kullanmak iyi bir fikir değildir (yöntem adınız açıkça belirtmediği sürece, örneğin TryParse()) iki nedenden dolayı. İlk olarak, bir hata koduna varsayılan yanıt, hata durumunu yoksaymak ve ne olursa olsun devam etmektir; İkincisi, geri dönüş kodlarını kullanarak bazı yöntemleri ve istisnaları kullanan diğer yöntemleri ve hangisinin hangisini unuttuğunu kolayca öğrenebilirsiniz. Aynı arabirimin birbiriyle değiştirilebilir iki farklı uygulamasının burada farklı yaklaşımlar aldığı kod tabanlarını bile gördüm.


2

İstisnalar, acil arama kodunun, arama yöntemi yapsa bile, kullanıma hazır olmayacağı ihtimalini temsil etmelidir . Örneğin, bir dosyadan bazı verileri okuyan kodun yasal olarak geçerli herhangi bir dosyanın geçerli bir kayıtla biteceğini ve kısmi bir kayıttan herhangi bir bilgi çıkarması gerekmediğini varsayabilir.

Okuma verisi yordamı istisnalar kullanmamışsa ancak okuma işleminin başarılı olup olmadığını basitçe bildirmişse, çağrı kodunun şöyle olması gerekir:

temp = dataSource.readInteger();
if (temp == null) return null;
field1 = (int)temp;
temp = dataSource.readInteger();
if (temp == null) return null;
field2 = (int)temp;
temp = dataSource.readString();
if (temp == null) return null;
field3 = temp;

Her faydalı parça için üç satır kod harcamak. Buna karşılık, readIntegerbir dosyanın sonuna denk gelince bir istisna atarsa ​​ve arayan kişi istisnayı basitçe geçebiliyorsa, kod şu şekilde olur:

field1 = dataSource.readInteger();
field2 = dataSource.readInteger();
field3 = dataSource.readString();

Çok daha basit ve daha temiz bir görünüme sahip, olayların normal şekilde çalıştığı duruma çok daha fazla önem veriyor. Hemen arayan durumlarda unutmayın ediyorum bir durum, çoğu zaman bir istisna atar olandan daha yararlı olacaktır hata kodu döndüren bir yöntem işlemek için bekliyor. Örneğin, bir dosyadaki tüm tamsayıları toplamak için:

do
{
  temp = dataSource.tryReadInteger();
  if (temp == null) break;
  total += (int)temp;
} while(true);

e karşı

try
{
  do
  {
    total += (int)dataSource.readInteger();
  }
  while(true);
}
catch endOfDataSourceException ex
{ // Don't do anything, since this is an expected condition (eventually)
}

Tamsayıları isteyen kod, bu aramalardan birinin başarısız olacağını bekliyor. Kodun olması, gerçekleşene kadar çalışacak olan sonsuz bir döngü kullanması, dönüş değeri üzerinden arızaları gösteren bir yöntem kullanmaktan çok daha az şıktır.

Sınıflar genellikle müşterilerinin hangi koşulları bekleyeceğini veya beklemeyeceğini bilmeyeceğinden, bazı arayanların bekleyeceği ve diğer arayanların beklemeyeceği şekilde başarısız olabilecek iki yöntem sürümü sunmak genellikle yararlı olur. Bunu yapmak, bu tür yöntemlerin her iki tür arayanla birlikte temiz bir şekilde kullanılmasını sağlayacaktır. Ayrıca, arayan kişi muhtemelen beklemiyorsa, "deneme" yöntemlerinin bile istisnalar atması gerektiğini unutmayın. Örneğin, tryReadIntegertemiz bir dosya sonu koşulu ile karşılaşırsa bir istisna atmamalı (arayan bunu beklemiyorsa, arayanreadInteger). Öte yandan, verilerin okunamaması durumunda bir istisna atması gerekir, çünkü onu içeren bellek çubuğu çıkarılmıştır. Bu tür olaylar her zaman bir olasılık olarak kabul edilmekle birlikte, acil arama kodunun cevap olarak faydalı bir şey yapmaya hazır olması muhtemel değildir; kesinlikle dosya sonu koşuluyla aynı şekilde bildirilmemelidir.


2

Yazılım yazarken en önemli şey okunabilir hale getirmektir. Diğer tüm hususlar, verimli hale getirilmesi ve doğru hale getirilmesi de dahil olmak üzere ikincildir. Okunabiliyorsa, gerisi bakımla ilgilenilebilir ve eğer okunamıyorsa o zaman atmadan daha iyi olursunuz. Bu nedenle, okunabilirliği arttırdığı zaman istisnalar atmalısınız.

Bazı algoritmalar yazarken, sadece gelecekte okuyacak olan kişiyi düşünün. Potansiyel bir problemin olabileceği bir yere geldiğinizde, okuyucuya şu anda bu problemle nasıl başa çıkacağınızı görmek ister misiniz yoksa okuyucu sadece algoritmaya başlamayı mı tercih eder?

Çikolatalı kek için bir tarif düşünmeyi severim. Size yumurtaları eklemenizi söyleyince, bir seçeneği vardır: ya yumurtaya sahip olduğunuzu varsayabilir ve tarifle devam edebilir ya da yumurtalarınız yoksa nasıl yumurta alabileceğinize dair bir açıklama başlatabilir. Yabani tavuk avlama teknikleriyle bütün bir kitabı doldurup, bir pasta pişirmenize yardımcı olabilir. Bu iyi, ama çoğu insan bu tarifi okumak istemeyecek. Çoğu insan sadece yumurtaların mevcut olduğunu varsaymayı ve tariflere devam etmeyi tercih ederdi. Bu, tarifleri yazarken yazarların yapması gereken bir yargılama çağrısıdır.

Neyin iyi bir istisna oluşturduğu ve hangi sorunların derhal ele alınması gerektiği konusunda hiçbir garantili kural yoktur, çünkü okuyucunuzun fikrini okumanızı gerektirir. Şimdiye kadar yapacağınız en iyi kurallar ve "istisnalar sadece istisnai durumlar içindir" oldukça iyi. Genellikle bir okuyucunuz yönteminizi okurken, yöntemin zamanın% 99'unda ne yapacağını arıyorlar ve kullanıcıların yasadışı girişe giriş yapma ve neredeyse hiç olmamış diğer şeylerle uğraşma gibi tuhaf köşe davaları ile uğraşmamış olmalarını tercih ediyorlardı. Yazılımınızın normal akışının doğrudan ortaya çıktığını, herhangi bir sorun çıkmamış gibi birbiri ardına gelen talimatı görmek istiyorlar.


2

Girişte bulabildiğimiz her hatayı, yalnızca birinciyi değil, rapor etmek gerekebilir.

Bu yüzden burada bir istisna atamazsın. İstisna, doğrulama işlemini hemen durdurur. Bu yüzden bunu halletmek için çok çalışmak gerekecek.

Kötü bir örnek:

Özel Dogdurumlar kullanarak sınıf için doğrulama yöntemi :

void validate(Set<DogValidationException> previousExceptions) {
    if (!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        DogValidationException disallowedName = new DogValidationException(Problem.DISALLOWED_DOG_NAME);
        if (!previousExceptions.contains(disallowedName)){
            throw disallowedName;
        }
    }
    if (this.legs < 4) {
        DogValidationException invalidDog = new DogValidationException(Problem.LITERALLY_INVALID_DOG);
        if (!previousExceptions.contains(invalidDog)){
            throw invalidDog;
        }
    }
    // etc.
}

Nasıl çağırılır:

Set<DogValidationException> exceptions = new HashSet<DogValidationException>();
boolean retry;
do {
    retry = false;
    try {
        dog.validate(exceptions);
    } catch (DogValidationException e) {
        exceptions.add(e);
        retry = true;
    }
} while (retry);

if(exceptions.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

Buradaki sorun, doğrulama işleminin tüm hataları almak için önceden bulunan istisnaları atlamak zorunda kalmasıdır. Yukarıdakiler işe yarayabilir, ancak bu istisnaların açıkça kötüye kullanılmasıdır . İstediğiniz doğrulama türü , veritabanına dokunulmadan önce yapılmalıdır . Yani hiçbir şeyi geri almaya gerek yok. Ve, doğrulama sonuçlarının doğrulama hataları olması muhtemeldir (umarım sıfırdır).

Daha iyi yaklaşım:

Yöntem çağrısı:

Set<Problem> validationResults = dog.validate();
if(validationResults.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

Doğrulama yöntemi:

Set<Problem> validate() {
    Set<Problem> result = new HashSet<Problem>();
    if(!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        result.add(Problem.DISALLOWED_DOG_NAME);
    }
    if(this.legs < 4) {
        result.add(Problem.LITERALLY_INVALID_DOG);
    }
    // etc.
    return result;
}

Neden? Tonlarca neden var ve çoğu neden diğer cevaplarda belirtildi. Basitleştirmek gerekirse: Başkaları tarafından okumak ve anlamak çok daha kolaydır . İkincisi, dogyanlış ayarladığını ona açıklamak için kullanıcı yığın izlerini göstermek ister misiniz ?

İkinci örnekteki işlem sırasında, doğrulayıcınız sıfır sorunla onaylanmış olsa bile , yine de bir hata oluşursa , bir istisna atmak doğru şeydir . Gibi: Hiçbir veritabanı bağlantısı yok, bu arada veritabanı girişi, başka biri tarafından değiştirildi veya bu şekilde.dog

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.