Sanırım aynı Bruce Eckel röportajını okudum - ve her zaman beni rahatsız etti. Aslında, argüman görüşmeci tarafından yapılmıştır (eğer gerçekten bahsettiğiniz gönderi ise) .NET ve C #'ın ardındaki MS dehası Anders Hejlsberg.
http://www.artima.com/intv/handcuffs.html
Fan, ben Hejlsberg ve eseri olmamasına rağmen, bu argüman beni her zaman sahte olarak vurdu. Temel olarak aşağıdakilere kadar kaynar:
"Kontrol edilen istisnalar kötü çünkü programcılar onları her zaman yakalayarak ve onları işten çıkararak kötüye kullanıyorlar, bu da kullanıcıya aksi halde sunulacak olan gizli ve göz ardı edilen sorunlara yol açıyor".
Tarafından "aksi Kullanıcıya sunulan" Bir çalışma zamanı özel kullanırsanız Yani tembel programcı sadece (boş bir catch bloğu ile alıcı yerine) göz ardı eder ve kullanıcı bunu görecek.
Argümanın özetinin özeti, "Programcılar bunları düzgün kullanmaz ve düzgün kullanmamak, sahip olmamaktan daha kötüdür" dir. .
Bu argümanın bir gerçeği var ve aslında, Java'da operatör geçersiz kılmalarını koymamak için Goslings motivasyonunun benzer bir argümandan geldiğinden şüpheleniyorum - programcıyı karıştırıyorlar çünkü sık sık istismar ediliyorlar.
Ama sonunda, bunu Hejlsberg'in ve muhtemelen iyi düşünülmüş bir karardan ziyade eksikliği açıklamak için yaratılan post-hoc bir argüman buldum.
Kontrol edilen istisnaların aşırı kullanımı kötü bir şey olsa da ve kullanıcılar tarafından özensiz işlemeye yol açarken, bunların doğru kullanımının API programcısının API istemci programcısına büyük fayda sağlamasına izin verdiğini iddia ediyorum.
Şimdi API programcısı, kontrol edilmiş istisnaları her yere atmamaya dikkat etmelidir, yoksa sadece istemci programlayıcıyı rahatsız edecektir. Çok tembel müşteri programcısı, (Exception) {}
Hejlsberg'in uyardığı gibi yakalamak için başvuracak ve tüm faydalar kaybedilecek ve cehennem ortaya çıkacak. Ancak bazı durumlarda, iyi kontrol edilmiş bir istisnanın yerini tutamaz.
Benim için klasik örnek, dosya açma API'sıdır. Diller tarihindeki her programlama dilinin (en azından dosya sistemlerinde) bir dosyayı açmanıza izin veren bir API'si vardır. Ve bu API'yı kullanan her istemci programcısı, açmaya çalıştıkları dosyanın mevcut olmamasıyla uğraşmak zorunda olduklarını biliyor. Bunu yeniden ifade edeyim: Bu API'yı kullanan her istemci programcısı , bu durumla uğraşmak zorunda olduklarını bilmelidir . Ve bir sürtünme var: API programcısı sadece yorum yaparak başa çıkmaları gerektiğini bilmelerine yardımcı olabilir mi yoksa gerçekten ısrar edebilirler mi? müşteriyle bununla başa çıkmak mi?
C'de deyim şuna benzer
if (f = fopen("goodluckfindingthisfile")) { ... }
else { // file not found ...
burada fopen
0 ve C döndürerek başarısızlığı gösterir (aptalca) 0'a bir boole gibi davranmanıza izin verir ve ... Temel olarak, bu deyimi öğrenirsiniz ve iyisinizdir. Ama ya bir çaylaksanız ve deyimi öğrenmediyseniz. Sonra, elbette,
f = fopen("goodluckfindingthisfile");
f.read(); // BANG!
ve zor yolu öğrenirler.
Burada yalnızca güçlü bir şekilde yazılmış dillerden bahsettiğimizi unutmayın: Güçlü bir şekilde yazılmış bir dilde bir API'nın ne olduğu hakkında net bir fikir var: Her biri için açık bir şekilde tanımlanmış bir protokolle kullanmanız için işlevselliğin (yöntemlerin) bir örneği.
Bu açıkça tanımlanmış protokol tipik olarak bir yöntem imzası ile tanımlanır. Burada fopen, bir dize (veya C durumunda bir char *) iletmenizi gerektirir. Başka bir şey verirseniz, derleme zamanı hatası alırsınız. Protokolü takip etmediniz - API'yi düzgün kullanmıyorsunuz.
Bazı (belirsiz) dillerde dönüş türü de protokolün bir parçasıdır. Eşdeğerini aramaya çalışırsanızfopen()
Bazı dillerdeki bir değişkene atamadan derleme zamanı hatası da alırsınız (bunu yalnızca geçersiz işlevlerle yapabilirsiniz).
Yapmaya çalıştığım nokta şudur: Statik olarak yazılan bir dilde API programcısı, herhangi bir bariz hata yaparsa istemci kodlarının derlenmesini engelleyerek istemciyi API'yi doğru şekilde kullanmaya teşvik eder.
(Ruby gibi dinamik olarak yazılan bir dilde, dosya adı olarak herhangi bir şey aktarabilir, kayan nokta söyleyebilirsiniz - ve derlenir. Burada yapılan argümanlar yalnızca statik olarak yazılmış diller için geçerlidir.)
Kontrol edilen istisnalar ne olacak?
İşte bir dosya açmak için kullanabileceğiniz Java API'lerinden biri.
try {
f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
// deal with it. No really, deal with it!
... // this is me dealing with it
}
Şu yakalamayı gördün mü? İşte bu API yönteminin imzası:
public FileInputStream(String name)
throws FileNotFoundException
Not FileNotFoundException
bir olduğunu kontrol istisna.
API programcısı size şunu söylüyor: "Bu yapıcıyı yeni bir FileInputStream oluşturmak için kullanabilirsiniz, ancak
a) gerekir String gibi dosya adı geçmektedir
) b gerekir "dosya zamanında bulunamadığı ihtimalini kabul
Ve endişelendiğim kadarıyla bütün mesele bu.
Anahtar temelde sorunun "programcının kontrolü dışında olan şeyler" olarak ifade ettiği şeydir. İlk düşüncem, API dışındaki şeyler anlamına gelmesiydi. programcılarının kontrolü . Ancak, doğru bir şekilde kullanıldığında kontrol edilen istisnalar, hem istemci programcısının hem de API programcısının kontrolünün dışında olan şeyler için gerçekten olmalıdır. Bu, kontrol edilen istisnaları kötüye kullanmamanın anahtarı olduğunu düşünüyorum.
Bence dosya açma bu noktayı güzel bir şekilde gösteriyor. API programcısı, API çağrıldığında varolmayan bir dosya adı verebileceğinizi ve size istediğinizi geri getiremeyeceklerini, ancak bir istisna atmaları gerekeceğini biliyor. Ayrıca, bunun oldukça düzenli olacağını ve istemci programcısının, aramayı yazdıkları sırada dosya adının doğru olmasını bekleyebileceğini biliyorlar, ancak çalışmalarının kontrolünün ötesindeki nedenlerden dolayı yanlış olabilir.
Bu yüzden API bunu açıkça ortaya koyuyor: Beni aradığınızda bu dosyanın mevcut olmadığı ve onunla daha iyi anlaştığınız durumlar olacak.
Bu bir karşı dava ile daha açık olur. Bir tablo API'sı yazdığımı düşünün. Bir yerde bu yöntemi içeren bir API ile tablo modeli var:
public RowData getRowData(int row)
Şimdi bir API programcısı olarak bazı istemcilerin satır veya tablonun dışında bir satır değeri için negatif bir değer geçirdiği durumlar olacağını biliyorum. Bu yüzden kontrol edilmiş bir istisna atmaya ve müşteri ile başa çıkmak için cazip olabilir:
public RowData getRowData(int row) throws CheckedInvalidRowNumberException
(Gerçekten "Checked" demezdim elbette.)
Bu, kontrol edilen istisnaların kötü kullanımıdır. İstemci kodu, her biri bir dene / yakala kullanmak zorunda kalacak satır verilerini almak için çağrılarla dolu olacak ve ne için? Kullanıcıya yanlış satır arandığını rapor edecekler mi? Muhtemelen hayır - tablo görünümümü çevreleyen kullanıcı arayüzü ne olursa olsun, kullanıcının geçersiz bir satırın istendiği bir duruma girmesine izin vermemelidir. Yani istemci programcısının bir hatası.
API programcısı, istemcinin bu tür hataları kodlayacağını ve bunu bir çalışma zamanı istisnasıyla işlemesi gerektiğini tahmin edebilir IllegalArgumentException
.
getRowData
Kontrollü bir istisna dışında , bu açıkça Hejlsberg'in tembel programcısına sadece boş yakalamalar ekleyecek bir durumdur. Bu olduğunda, geçersiz satır değerleri test cihazı veya istemci geliştirici hata ayıklaması için bile açık olmayacak, bunun yerine kaynağını belirlemek zor olan takılma hatalarına yol açacaktır. Arianne roketleri fırlatmadan sonra patlayacak.
Tamam, işte sorun: İşaretli istisnanın FileNotFoundException
sadece iyi bir şey değil, API programcıları araç kutusunda API'yi istemci programcı için en yararlı şekilde tanımlamak için gerekli bir araç olduğunu söylüyorum . Ancak, CheckedInvalidRowNumberException
kötü programlamaya yol açan büyük bir rahatsızlıktır ve kaçınılmalıdır. Ama farkı nasıl söyleyebilirim.
Sanırım bu tam bir bilim değil ve sanırım Hejlsberg'in argümanının altını çiziyor ve belki de belli bir dereceye kadar haklı. Ama bebeği burada banyo suyuyla atmaktan mutlu değilim, bu yüzden iyi kontrol edilmiş istisnaları kötülerden ayırt etmek için buraya bazı kurallar çıkarmama izin verin:
Müşterinin kontrolü dışında veya Kapalı vs Açık:
İşaretli istisnalar yalnızca hata durumunun hem API'nin hem de istemci programcısının kontrolü dışında olduğu durumlarda kullanılmalıdır . Bu, sistemin ne kadar açık veya kapalı olduğu ile ilgilidir. Bir de kısıtlı müşteri programcı düğmeleri, klavye komutları vs tümü üzerinde, diyelim ki, kontrole sahip UI tablo görünümü (kapalı bir sistem), bir istemci programlama hata olduğunu gelen eklenti ve sil satırlar o veri almaya çalışırsa o var olmayan bir satır. Çok sayıda kullanıcının / uygulamanın dosya ekleyip silebildiği dosya tabanlı bir işletim sisteminde (açık bir sistem), istemcinin istediği dosyanın bilgisi olmadan silinmiş olması mümkündür, bu yüzden onunla başa çıkmaları beklenir. .
Her yerde kullanım:
İşaretli istisnalar, istemci tarafından sık yapılan bir API çağrısında kullanılmamalıdır. Sık sık, müşteri kodunda birçok yerde kastediyorum - sık sık değil. Bu nedenle, bir istemci kodu aynı dosyayı çok fazla açmaya çalışmaz, ancak tablo görünümüm RowData
farklı yöntemlerden her yeri alır . Özellikle, bir sürü kod yazacağım
if (model.getRowData().getCell(0).isEmpty())
ve her seferinde denemek / yakalamak sarmak acı verici olacaktır.
Kullanıcıyı Bilgilendirme:
İşaretli istisnalar, son kullanıcıya faydalı bir hata mesajı sunulduğunu hayal edebileceğiniz durumlarda kullanılmalıdır. Bu "ve bu olduğunda ne yapacaksın?" soru ben yukarıda yetiştirdim. Ayrıca madde 1 ile de ilgilidir. İstemci-API sisteminizin dışındaki bir şeyin dosyanın orada olmamasına neden olabileceğini tahmin edebileceğiniz için, kullanıcıya makul bir şekilde bunu anlatabilirsiniz:
"Error: could not find the file 'goodluckfindingthisfile'"
Yasadışı satır numaranıza dahili bir hata neden olduğu ve kullanıcının hatasından kaynaklandığı için, onlara verebileceğiniz hiçbir yararlı bilgi yoktur. Uygulamanız çalışma zamanı istisnalarının konsola girmesine izin vermezse, muhtemelen onlara aşağıdaki gibi çirkin bir mesaj verir:
"Internal error occured: IllegalArgumentException in ...."
Kısacası, istemci programlayıcınızın istisnanızı kullanıcıya yardımcı olacak bir şekilde açıklayabileceğini düşünmüyorsanız, muhtemelen işaretli bir istisna kullanmamalısınız.
Bunlar benim kurallarım. Biraz uyuştu ve şüphesiz istisnalar olacak (lütfen eğer onları iyileştirmem için bana yardım et). Ancak benim asıl argümam, FileNotFoundException
kontrol edilen istisnanın, parametre türleri kadar API sözleşmesinin bir parçası kadar önemli ve yararlı olduğu durumlar olduğu yönündedir. Bu yüzden sadece yanlış kullanıldığından vazgeçmemeliyiz.
Üzgünüm, bunu bu kadar uzun ve waffly yapmak istememiştim. İki öneriyle bitireyim:
Y: API programcıları: kullanışlılıklarını korumak için işaretli istisnaları az miktarda kullanın. Şüphe duyduğunuzda kontrol edilmeyen bir istisna kullanın.
B: Müşteri programcıları: geliştirme sürecinizin başında sarılmış bir istisna (google it) oluşturma alışkanlığı edinin. JDK 1.4 ve üzeri bunun için bir kurucu sağlar RuntimeException
, ancak kendiniz de kolayca oluşturabilirsiniz. İşte kurucu:
public RuntimeException(Throwable cause)
Daha sonra kontrol edilen bir istisnayı ele almanız gerektiğinde ve tembel hissettiğinizde (veya API programcısının kontrol edilen istisnayı ilk etapta aşırı derecede hevesli olduğunu düşünüyorsanız) alıştırın, sadece istisnayı yutmayın, sarın ve yeniden düşünün.
try {
overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
throw new RuntimeException(exception);
}
Bunu IDE'nizin küçük kod şablonlarından birine koyun ve tembel hissettiğinizde kullanın. Bu şekilde, kontrol edilen istisnayı gerçekten ele almanız gerekiyorsa, sorunu çalışma zamanında gördükten sonra geri gelmek ve onunla başa çıkmak zorunda kalacaksınız. Çünkü, inanın bana (ve Anders Hejlsberg), bu TODO'ya asla geri dönmeyeceksiniz.
catch (Exception e) { /* TODO deal with this at some point (yeah right) */}