İstisnalar - “ne oldu” ve “ne yapmalı”


19

Kod tüketicisinin beklenmedik davranışları faydalı bir şekilde işlemesine izin vermek için istisnalar kullanıyoruz. Genellikle "ne oldu" senaryosu etrafında FileNotFound(belirttiğiniz dosyayı bulamadık) veya ZeroDivisionError( 1/0işlemi gerçekleştiremedik ) istisnalar oluşur .

Ya tüketicinin beklenen davranışını belirleme olasılığı varsa?

Örneğin, fetchHTTP isteğini yerine getiren ve alınan verileri döndüren bir kaynağımız olduğunu düşünün . Ve yerine benzeri hataların ServiceTemporaryUnavailableveya RateLimitExceededsadece bir gündeme getireceğini RetryableErrorsadece yeniden deneme isteği olmalı ve belirli başarısızlık umurumda değil o tüketiciyi düşündüren. Yani, temelde arayan kişiye bir eylem öneriyoruz - "ne yapmalı".

Bunu sık sık yapmıyoruz çünkü tüketicilerin tüm kullanımlarını bilmiyoruz. Ama bunun bir arayan için en iyi eylem yolunu bildiğimiz belirli bir bileşen olduğunu düşünün - o zaman "ne yapmalı" yaklaşımından faydalanmalı mıyız?


3
HTTP bunu zaten yapmıyor mu? 503 yanıtlamak için geçici bir başarısızlıktır, bu nedenle istekte bulunanın yeniden denemesi gerekir, 404 temel bir eksikliktir, bu yüzden yeniden denemek mantıklı değildir, 301 "kalıcı olarak taşındı" anlamına gelir, bu yüzden tekrar denemeniz gerekir, ancak farklı bir adresle vb.
Kilian Foth

7
Birçok durumda, "ne yapacağımızı" gerçekten biliyorsak, bilgisayarı otomatik olarak yapabilir ve kullanıcının hiçbir şeyin yanlış gittiğini bile bilmesine gerek kalmaz. Tarayıcım bir 301 aldığında, bana sormadan yeni adrese gittiğini varsayıyorum.
Ixrec

@Ixrec - aynı fikre sahipti. ancak, tüketici başka bir istek beklemek ve öğeyi görmezden gelmek veya tamamen başarısız olmak istemeyebilir.
Roman Bodnarchuk

1
@RomanBodnarchuk: Kabul etmiyorum. Bir kişinin Çince konuşmak için Çince bilmesine gerek olmaması gerektiğini söylemek gibi. HTTP bir protokoldür ve hem istemciden hem de sunucunun bunu bilmesi ve izlemesi beklenir. Protokol böyle çalışır. Sadece bir taraf biliyor ve ona uyuyorsa, iletişim kuramazsınız.
Chris Pratt

1
Dürüst olmak gerekirse, istisnalarınızı bir catch bloğu ile değiştirmeye çalışıyorsunuz. Bu nedenle istisnalara taşındık - artık yok if( ! function() ) handle_something();, ancak arama bağlamını bildiğiniz bir yerde hatayı işleyebilmemiz - yani bir istemciye sunucunuz başarısız olursa veya bağlantı kesilirse otomatik olarak yeniden yükleme yapması için bir sys yöneticisini aramasını söyleyin, ancak sizi uyarın arayanın başka bir mikro servis olması durumunda. Yakalama bloklarının yakalamayı ele almasına izin verin.
Sebb

Yanıtlar:


47

Ama bunun bir arayan için en iyi eylem yolunu bildiğimiz belirli bir bileşen olduğunu düşünün.

Bu, neredeyse her zaman arayanlardan en az biri için başarısız olur ve bu davranış inanılmaz derecede tahriş edicidir. En iyi bildiğinizi varsaymayın. Kullanıcılarınıza bu konuda ne yapmaları gerektiğini düşündüğünüzü değil, neler olduğunu anlatın. Birçok durumda, aklı başında bir eylemin ne olması gerektiği zaten açıktır (ve eğer değilse, kullanım kılavuzunuzda bir öneri yapın).

Örneğin, sorunuzda verilen istisnalar bile kırık varsayımınızı gösterir: a ServiceTemporaryUnavailable"daha sonra tekrar deneyin" e RateLimitExceededeşittir ve "woah chill out, belki zamanlayıcı parametrelerinizi ayarlayıp birkaç dakika içinde tekrar deneyin" e eşittir. Ancak kullanıcı, bir tür alarmı ServiceTemporaryUnavailable(bir sunucu problemini gösterir) değil de yükseltmek isteyebilir RateLimitExceeded(değil).

Onlara seçim yap .


4
Katılıyorum. Sunucu bilgileri yalnızca düzgün bir şekilde iletmelidir. Öte yandan belgeler, bu gibi durumlarda uygun eylem yolunu açıkça belirtmelidir.
Neil

1
Buradaki küçük bir kusur, bazı bilgisayar korsanları için, bazı istisnalar onlara kodunuzun ne yaptığı hakkında oldukça fazla şey söyleyebilir ve bunu sömürmenin yollarını bulmak için kullanabilirler.
Pharap

3
@Pharap Bilgisayar korsanlarınızın hata mesajı yerine istisnanın kendisine erişimi varsa, zaten kaybolmuş demektir.
corsiKa

2
Bu yanıtı beğendim, ancak istisnalar için bir gereklilik olarak gördüğüm şey eksik ... nasıl kurtarılacağını biliyorsan, bir istisna olmaz! Bir istisna yalnızca bu konuda bir şey yapamadığınız zaman olmalıdır: geçersiz giriş, geçersiz durum, geçersiz güvenlik - bunları programlı olarak düzeltemezsiniz.
corsiKa

1
Kabul. Bir yeniden denemenin mümkün olup olmadığını belirtmek konusunda ısrar ediyorsanız , ilgili somut istisnaları her zaman devralabilirsiniz RetryableError.
sapi

18

Uyarı! C ++ programcısı, istisna işlemenin kesinlikle başka bir dil ile ilgili bir soruyu cevaplamaya çalışırken nasıl yapılacağına dair muhtemelen farklı fikirlerle geliyor!

Bu fikir verildiğinde:

Örneğin, HTTP isteğini yerine getiren ve alınan verileri döndüren bir kaynağa sahip olduğumuzu düşünün. Ve ServiceTemporaryUnavailable veya RateLimitExceeded gibi hatalar yerine, tüketiciye sadece talebi yeniden denemesi ve belirli bir arızayı önemsememesi gerektiğini belirten bir RetryableError oluşturacağız.

... önerebileceğim bir şey, kodunuzun genelliğini bozabilecek veya istisnalar için çok sayıda "çeviri noktası" gerektirebilecek şekilde yanıt vermek için bir hata bildirme eylemleriyle ilgili endişeleri karıştırıyor olabilirsiniz. .

Örneğin, bir dosyayı yüklemeyi içeren bir işlemi modellediğimde, bunun birkaç nedeni olabilir. Belki de dosyanın yüklenmesi kullanıcının makinesinde olmayan bir eklentinin yüklenmesini içerir. Belki de dosya bozulmuş ve ayrıştırılırken bir hatayla karşılaştık.

Ne olursa olsun, eylemin seyrini kullanıcıya ne olduğunu bildirmek ve ona ne yapmak istediğini sormak olduğunu varsayalım ("tekrar dene, başka bir dosya yükle, iptal et").

Atıcı ve Avcı

Bu eylem, bu durumda ne tür bir hatayla karşılaştığımızdan bağımsız olarak uygulanır. Genel olarak bir ayrıştırma hatası fikrine gömülmez, bir eklentiyi yükleyememe genel fikrine gömülü değildir. Bir dosya yüklemenin kesin bağlamı (bir dosya yükleme ve başarısızlık kombinasyonu) sırasında bu tür hatalarla karşılaşma fikrine gömülüdür. Yani tipik olarak, kabaca konuşarak, catcher'satılan bir istisna (örneğin: kullanıcıya seçeneklerle sorma) yanıt olarak eylemin seyrini belirleme sorumluluğu olarak görüyorum thrower's.

Başka bir deyişle, throwistisnaları olan siteler , özellikle de atanan işlevler genel olarak uygulanabilirse, bu tür bağlamsal bilgilerden yoksundur. Bu bilgilere sahip olduklarında tamamen dejenere olmuş bir bağlamda bile, throwsiteye gömerek kurtarma davranışı açısından kendinizi köşeye sıkıştırırsınız . Bir catcheylemin seyrini belirlemek için genellikle en fazla bilgiye sahip olan sitelerdir ve bu işlemin söz konusu işlem için değişmesi gerekip gerekmediğini değiştirmek için size merkezi bir yer verir.

Artık neyin yanlış olduğunu bildirmekle kalmayıp ne yapacağınızı belirlemeye çalışırken istisnalar atmaya başladığınızda, bu kodunuzun genelliğini ve esnekliğini bozabilir. Ayrıştırma hatası her zaman bu tür bir isteme yol açmamalıdır, bu tür bir istisnanın atıldığı bağlama göre değişir (atıldığı işlem).

Kör Atıcı

Sadece genel olarak, istisna işleme tasarımının çoğu genellikle kör bir atıcı fikri etrafında döner. İstisnanın nasıl yakalanacağını veya nerede yakalanacağını bilmiyor. Aynı durum, manuel hata yayılımı kullanan eski hata kurtarma biçimleri için bile geçerlidir. Hatalarla karşılaşan siteler, kullanıcının bir işlem biçimini içermez, yalnızca ne tür bir hatayla karşılaşıldığını bildirmek için yalnızca minimum bilgileri yerleştirir.

Ters Sorumluluklar ve Yakalayıcının Genelleştirilmesi

Bunu daha dikkatli bir şekilde düşünürken, bunun bir cazibe olabileceği bir kod tabanı hayal etmeye çalışıyordum. Hayal gücüm (muhtemelen yanlış), ekibinizin hala burada "tüketici" rolünü oynadığı ve arama kodunun çoğunu uyguladığı. Belki de tryhepsi aynı hata kümelerine girebilecek birçok farklı işleminiz (çok sayıda blok) var ve hepsi tasarım açısından bakıldığında tek tip bir kurtarma eylemine yol açmalı.

Lightness Races in Orbit'sİnce cevabın akıllıca tavsiyelerini göz önünde bulundurarak (gerçekten gelişmiş bir kütüphane odaklı zihniyetten geldiğini düşünüyorum), yine de "ne yapmalı" istisnaları atmak için cazip olabilirsiniz, sadece işlem kurtarma sitesine daha yakın.

Buradan, aslında "ne yapmalı" endişelerini merkezileştiren, ancak yine de yakalama bağlamında, aracı, ortak bir işlem gerçekleştirme sitesi bulmak mümkün olabilir.

resim açıklamasını buraya girin

Bu, yalnızca bu dış işlemlerin tümünün kullandığı bir tür genel işlev tasarlayabiliyorsanız (ör: çağrılacak başka bir işlevi giren bir işlev veya karmaşık alıcı siteyi yapan bu aracı işlem sitesini modelleyen geçersiz kılınabilen davranışa sahip soyut bir işlem tabanı sınıfı tasarlayabiliyorsanız geçerlidir. ).

Yine de, çeşitli olası hatalara yanıt olarak ve hala atmak yerine yakalama bağlamında kullanıcı hareket tarzını merkezileştirmekten sorumlu olabilir. Basit bir örnek (Python-ish sözde kodu, ve ben en ufak bir tecrübeli Python geliştiricisi değilim, bu yüzden bu konuda daha deyimsel bir yol olabilir):

def general_catcher(task):
    try:
       task()
    except SomeError1:
       # do some uniformly-designed recovery stuff here
    except SomeError2:
       # do some other uniformly-designed recovery stuff here
    ...

Umarım daha iyi bir isimle general_catcher]. Bu örnekte, hangi görevi gerçekleştireceğinizi içeren, ancak ilgilendiğiniz tüm istisna türleri için genelleştirilmiş / birleştirilmiş yakalama davranışından yararlanan bir işlevi geçirebilir ve "ne yapmalı" bölümünü genişletmeye veya değiştirmeye devam edebilirsiniz. bu merkezi konumdan ve yine de catchbunun genellikle teşvik edildiği bir bağlamda hoşunuza gidiyor . Hepsinden iyisi, fırlatma alanlarının kendilerini "ne yapmalı" ("kör atıcı" kavramını koruyarak) ile ilişkilendirmekten alıkoyabiliriz.

Burada bu önerilerin hiçbirini yararlı bulmazsanız ve yine de "ne yapmalı" istisnaları atmak için güçlü bir cazibe varsa, esas olarak bunun en azından çok anti-deyimsel olduğunun ve genelleştirilmiş bir zihniyetin potansiyel olarak cesaret kırıcı olduğunun farkında olun.


2
+1. Daha önce olduğu gibi "Kör Atıcı" fikrini hiç duymadım, ancak istisna yönetimini nasıl düşündüğüme uyuyor: hatanın oluştuğunu belirtin, nasıl ele alınacağını düşünmeyin. Tüm yığıntan sorumlu olduğunuzda, sorumlulukları temiz bir şekilde ayırmak zor (ama önemli!) Ve arayan kişi sorunları bildirmekten ve arayan kişi sorunları ele almaktan sorumludur. Callee sadece ne yapması istendiğini bilir, nedenini değil. Kullanım hataları 'neden' bağlamında, dolayısıyla: arayanda yapılmalıdır.
Sjoerd Job Postmus

1
(Ayrıca: Cevabınızın C ++ 'a özgü olduğunu düşünmüyorum, ancak genel olarak istisna işleme uygulanabilir)
Sjoerd Job Postmus

1
@SjoerdJobPostmus Yay teşekkürler! "Kör Atıcı" sadece buraya geldiğim aptal bir benzetmedir - karmaşık teknik kavramları sindirmekte çok akıllı ya da hızlı değilim, bu yüzden genellikle kendi anlayışımı açıklamaya ve geliştirmeye çalışmak için küçük görüntüler ve benzetmeler bulmak istiyorum şeylerin. Belki bir gün çok sayıda karikatür çizimleriyle dolu küçük bir programlama kitabı yazmaya çalışabilirim. :-D

1
Heh, bu hoş bir görüntü. Göz bağı giyen ve beyzbol şeklindeki istisnaları dışarı atan küçük bir çizgi film karakteri, onları kimin yakalayacağından (veya hiç yakalanıp yakalanmayacaklarından) emin değil, ancak kör atıcı olarak görevlerini yerine getiriyor.
Blacklight Parlayan

1
@ DrunkCoder: Lütfen yayınlarınızı tahrip etmeyin. İnternet'te zaten yeterli miktarda bornoz var. Silme işleminiz için iyi bir nedeniniz varsa, gönderinizi moderatörlerin dikkatine sunduğunuzda işaretleyin ve durumunuzu belirtin.
Robert Harvey

2

Bence çoğu zaman, bu durumların nasıl ele alınacağını söyleyen işleve argüman iletmek daha iyi olur.

Örneğin, bir işlevi düşünün:

Response fetchUrl(URL url, RetryPolicy retryPolicy);

RetryPolicy.noRetries () veya RetryPolicy.retries (3) ya da her neyse geçebilirim. Yeniden ödenebilir bir başarısızlık durumunda, yeniden denenmesi gerekip gerekmediğine karar vermek için politikaya danışacaktır.


Bununla birlikte, istisnaların çağrı sitesine geri gönderilmesiyle pek bir ilgisi yoktur. Biraz farklı bir şeyden bahsediyorsunuz, ki bu güzel ama sorunun bir parçası değil ..
Monica ile Lightness Races

@LightnessRacesinOrbit, aksine. İstisnaları çağrı sitesine geri gönderme fikrine bir alternatif olarak sunuyorum. OP örneğinde, fetchUrl bir RetryableException atar ve bunun yerine fetchUrl ne zaman yeniden denemek gerektiğini söylemelisiniz diyorum.
Winston Ewert

2
@WinstonEwert: LightnessRacesinOrbit ile hemfikir olmama rağmen, aynı zamanda fikrinizi de görüyorum, ama aynı kontrolü temsil etmenin farklı bir yolu olarak düşünüyorum. Ancak, muhtemelen geçmek isteyeceğinizi düşünün new RetryPolicy().onRateLimitExceeded(STOP).onServiceTemporaryUnavailable(RETRY, 3), çünkü bunun RateLimitExceededfarklı bir şekilde ele alınması gerekebilir ServiceTemporaryUnavailable. Bunu yazdıktan sonra, düşüncem: daha iyi bir istisna atmak, çünkü daha esnek kontrol sağlıyor.
Sjoerd Job Postmus

@SjoerdJobPostmus, bence buna bağlı. Bu oran sınırlama ve yeniden deneme mantığı, kütüphanenizden bu yana iyi olabilir, bu durumda yaklaşımımın mantıklı olduğunu düşünüyorum. Bunu sadece arayanınıza bırakmak daha mantıklıysa, elbette atın.
Winston Ewert
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.