İkisini de kullanmalısın. Önemli olan her birinin ne zaman kullanılacağına karar vermektir .
Bir vardır istisnalar bariz bir seçimdir birkaç senaryo :
Bazı durumlarda , hata koduyla hiçbir şey yapamazsınız ve bunu çağrı yığınında daha üst bir düzeyde işlemeniz gerekir , genellikle yalnızca hatayı günlüğe kaydedin , kullanıcıya bir şey görüntüleyin veya programı kapatın. Bu gibi durumlarda, hata kodları, hata kodlarını manuel olarak seviye bazında kabarcıklandırmanızı gerektirir; bu, istisnalarla yapılması çok daha kolaydır. Mesele şu ki, bu beklenmedik ve başa çıkılamaz durumlar için.
Yine de 1. durumla ilgili (beklenmedik ve idare edilemeyen bir şey olduğunda, sadece günlüğe kaydetmek istemezsiniz), istisnalar yardımcı olabilir çünkü bağlamsal bilgi ekleyebilirsiniz . Örneğin, alt düzey veri yardımcılarımda bir SqlException alırsam, bu hatayı düşük düzeyde (hataya neden olan SQL komutunu bildiğim yerde) yakalamak isteyeceğim , böylece bu bilgileri yakalayabilir ve ek bilgilerle yeniden atabilirim . Lütfen buradaki sihirli kelimeye dikkat edin: yeniden atın ve yutmayın .
İstisna işlemenin ilk kuralı: istisnaları yutmayın . Ayrıca, içteki yakalamanın hiçbir şeyi kaydetmesine gerek olmadığını unutmayın çünkü dış yakalama tüm yığın izine sahip olacak ve bunu kaydedebilir.
Bazı durumlarda bir dizi komutunuz vardır ve bunlardan herhangi biri başarısız olursa , bu kurtarılamaz bir durum (atılmalıdır) veya kurtarılabilir bir durum (bu durumda yapabilirsiniz) olsun veya olmasın kaynakları temizlemeniz / imha etmeniz gerekir (*). yerel olarak veya arayan kodunda ele alın ancak istisnalara ihtiyacınız yoktur). Açıkçası, her yöntemden sonra hata kodlarını test etmek yerine tüm bu komutları tek bir denemede koymak ve nihayet bloğunda temizlemek / imha etmek çok daha kolaydır. Lütfen hatanın ortaya çıkmasını istiyorsanız (ki muhtemelen istediğiniz şey budur), onu yakalamanıza bile gerek olmadığını unutmayın - sadece temizlemek / imha etmek için sonunu kullanın - sadece isterseniz catch / retrow'u kullanmalısınız bağlamsal bilgi eklemek için (bkz. madde 2).
Bir örnek, bir işlem bloğunun içindeki SQL ifadeleri dizisi olabilir. Yine, bu aynı zamanda "başa çıkılamaz" bir durum, erken yakalamaya karar verseniz bile (en üste köpürmek yerine yerel olarak tedavi edin), en iyi sonucun her şeyi iptal etmek veya en azından büyük bir kısmı iptal etmek olduğu durumlarda hala ölümcül bir durumdur . sürecin bir parçası.
(*) Bu, on error goto
eski Visual Basic'te kullandığımız gibidir
Yapıcılarda yalnızca istisnalar atabilirsiniz.
Arayanın bazı işlemler yapabileceği / GEREKTİĞİ bazı bilgileri döndürdüğünüz diğer tüm durumlarda , dönüş kodlarını kullanmak muhtemelen daha iyi bir alternatiftir. Bu, beklenen tüm "hataları" içerir , çünkü muhtemelen hemen arayan tarafından ele alınmaları gerekir ve yığında çok fazla seviye yukarı çıkmasına gerek kalmaz.
Elbette, beklenen hataları istisna olarak ele almak ve hemen bir seviye yukarıdan yakalamak her zaman mümkündür ve ayrıca bir deneme yakalamasında her kod satırını kapsamak ve olası her hata için eylemler gerçekleştirmek de mümkündür. IMO, bu kötü bir tasarım, sadece çok daha ayrıntılı olduğu için değil, özellikle kaynak kodu okunmadan ortaya çıkabilecek olası istisnalar açık olmadığı için - ve herhangi bir derin yöntemden istisnalar atılarak görünmez gotoslar yaratılabileceği için . Kodun okunmasını ve incelenmesini zorlaştıran birden çok görünmez çıkış noktası oluşturarak kod yapısını bozarlar. Başka bir deyişle, akış kontrolü olarak istisnaları asla kullanmamalısınız, çünkü bunu başkalarının anlaması ve sürdürmesi zor olacaktır. Test için olası tüm kod akışlarını anlamak bile zor olabilir.
Tekrar: Doğru temizleme / imha için, hiçbir şey yakalamadan try-final'i kullanabilirsiniz .
Dönüş kodlarıyla ilgili en popüler eleştiri, "birisi hata kodlarını görmezden gelebilir, ancak aynı anlamda birileri istisnaları da yutabilir. Kötü istisnaları ele almak her iki yöntemde de kolaydır . Ancak iyi hata kodu tabanlı program yazmak hala çok daha kolaydır. İstisnaya dayalı bir program yazmaktansa ve herhangi bir nedenle tüm hataları (eski olanlar on error resume next
) görmezden gelmeye karar verirse , bunu dönüş kodlarıyla kolayca yapabilirsiniz ve bunu çok fazla deneme yakalama şablonu olmadan yapamazsınız.
Dönüş kodlarıyla ilgili en popüler ikinci eleştiri, "ortaya çıkmasının zor olmasıdır" - ancak bunun nedeni, insanların istisnaların kurtarılamaz durumlar için olduğunu, hata kodlarının ise olmadığını anlamadıklarıdır.
İstisnalar ve hata kodları arasında karar vermek gri bir alandır. Hatta bazı yeniden kullanılabilir iş yöntemlerinden bir hata kodu almanız gerekebilir ve sonra bunu bir istisnaya (muhtemelen bilgi ekleyerek) sarmaya karar verip kabarmasına izin verin. Ancak TÜM hataların istisna olarak atılması gerektiğini varsaymak bir tasarım hatasıdır.
Özetlersek:
Beklenmedik bir durumla karşılaştığımda, yapacak pek bir şey olmadığında ve genellikle büyük bir kod bloğunu veya hatta tüm işlemi veya programı iptal etmek istediğimizde istisnalar kullanmayı seviyorum. Bu, eski "hata durumunda gitme" gibidir.
Arayan kodunun bazı eylemler yapabileceği / yapması gereken beklenen durumlar olduğunda dönüş kodlarını kullanmayı seviyorum. Bu, çoğu iş yöntemini, API'leri, doğrulamaları vb. İçerir.
İstisnalar ve hata kodları arasındaki bu fark, normal beklenen durumlar hata olarak döndürülürken, ölümcül beklenmedik durumlar için "panik" kullanan GO dilinin tasarım ilkelerinden biridir.
Yine de GO ile ilgili olarak , aynı anda bir hata veya başka bir şey döndürebileceğiniz için, dönüş kodlarının kullanılmasında çok yardımcı olan bir şey olan çoklu dönüş değerlerine de izin verir . C # / Java'da bunu parametreler, Tuple'lar veya (en sevdiğim) Generics olmadan başarabiliriz, bunlar numaralandırmalarla birleştirildiğinde arayanlara açık hata kodları sağlayabilir:
public MethodResult<CreateOrderResultCodeEnum, Order> CreateOrder(CreateOrderOptions options)
{
....
return MethodResult<CreateOrderResultCodeEnum>.CreateError(CreateOrderResultCodeEnum.NO_DELIVERY_AVAILABLE, "There is no delivery service in your area");
...
return MethodResult<CreateOrderResultCodeEnum>.CreateSuccess(CreateOrderResultCodeEnum.SUCCESS, order);
}
var result = CreateOrder(options);
if (result.ResultCode == CreateOrderResultCodeEnum.OUT_OF_STOCK)
// do something
else if (result.ResultCode == CreateOrderResultCodeEnum.SUCCESS)
order = result.Entity; // etc...
Yöntemime yeni bir olası dönüş eklersem, tüm arayanların, örneğin bir switch ifadesinde bu yeni değeri kapsayıp kapsamadığını bile kontrol edebilirim. Bunu istisnalar dışında gerçekten yapamazsınız. Dönüş kodlarını kullandığınızda, genellikle olası tüm hataları önceden bilir ve bunları test edersiniz. İstisnalar dışında, genellikle ne olabileceğini bilemezsiniz. Numaralandırmaları istisnaların içine sarmak (Jenerikler yerine) bir alternatiftir (her yöntemin atacağı istisnaların türü açık olduğu sürece), ancak IMO yine de kötü bir tasarımdır.