Giriş
Exception nesneye yönelik programlama dillerinin ayrılmaz bir parçası. C++, Java, C# gibi genel amaçlı dillerde sıkça karşımıza çıkıyor. Yeni bir akım olarak şu dikkatimi çekti. Modern programlama dillerinde artık exception kullanılmıyor. Örneğin Swift ve Go dillerinde exception yok.
Programlama dilinden bağımsız olarak aldığım notlar aşağıda.
Exception'ın Faydaları
Hatayı Telafi Etme
Exception nesneye yönelik programlama dillerinin ayrılmaz bir parçası. C++, Java, C# gibi genel amaçlı dillerde sıkça karşımıza çıkıyor. Yeni bir akım olarak şu dikkatimi çekti. Modern programlama dillerinde artık exception kullanılmıyor. Örneğin Swift ve Go dillerinde exception yok.
Programlama dilinden bağımsız olarak aldığım notlar aşağıda.
Exception'ın Faydaları
Hatayı Telafi Etme
Örnek
Şöyle yaparız. Bu örnekte eğer specific_error1 geldiyse hata loglanıyor ve recover işlemi yapılıyor. Eğer specific_error2 geldiyse, hata loglanıp tekrar fırlatılıyor.
try:
do_one_thing()
do_another_thing()
do_more_risky_stuff()
except specific_error1:
log_error()
recover_from_specific_problem_regardless_of_where_it_came_from()
except specific_error2:
log_error()
raise # Don't know how to recover at this level so kick it upstairs
Hata Mesajı
Exception iletilmek istenen mesajı içerir. Böylece hata çok daha rahat anlaşılabilir. Mesaj C ve C++'ta bulunan errno değişkenine göre çok büyük kolaylık sağlar.
Örnek
Elimizde şöyle bir kod olsun. Bu kod hata kodunun exception ile sarmalanması gösteriliyor.
Exception hatanın oluştuğu yere kadar call stack'i gösterir. Debug işlemi için büyük kolaylık sağlar.
Nested Try Catch
Bazen iç içe geçmiş try-catch blokları görebiliyoruz. İlk başta ben de çok saçma olduğunu düşünmüştüm. Ancak daha göze hoş görünmese de sonra bu kullanımın bir yeri olduğunu anladım.
Örnek
Elimizde şöyle bir kod olsun. Hata alınınca işlemi geri sarmak için başka çare olmadığı gösterilmiş.
Control Flow
Örnek - Listenin Sonuna Gelmek
Örneğin veritabanından veri çekerken NoRowsException atılması yanlıştır. Çünkü bir hata alınmamış sadece verinin sonuna gelinmiştir.
Result Object
Bazen exception Result Object yerine kullanılıyor. Açıklaması şöyle.
Elimizde şöyle bir kod olsun. Bu kod parçasında key ile arama yapılıyor, sonuç bulunamazsa lokal exception atılıp akış değiştiriliyor. Ancak exception sadece ne yapacağımızı bilmediğimizde atılmalı.
Örnek ver
Poor Error Handling: Overly Broad Catch
Örnek ver
Poor Error Handling: Empty Catch Block - Exception'ın Sessizce Yutulması
Açıklaması şöyle.
Elimizde şöyle bir kod olsun. Bu kodda hata oluştuğunu anlamanın bir yolu yok.
Elimizde şöyle bir kod olsun. Burada exception fırlatıldığı halde işlem tamamlanıyor.
Bu kullanım şeklinde exception sadece loglamak amaçlı olarak yakalanıyor ve tekrar fırlatılıyor. Bu tekniğin aynı exception'ının defalarca loglanmasına yol açacağı açıklanıyor. Benzer bir cevabı burada da görebilirsiniz. Exception-Handling Antipatterns sayfasında bazı yanlış kullanım örnekleri de var.
Exception iletilmek istenen mesajı içerir. Böylece hata çok daha rahat anlaşılabilir. Mesaj C ve C++'ta bulunan errno değişkenine göre çok büyük kolaylık sağlar.
Örnek
Elimizde şöyle bir kod olsun. Bu kod hata kodunun exception ile sarmalanması gösteriliyor.
public class OperationException:Exception {
public int ErrorCode { get; private set; }
public string ErrorDescription { get; private set; }
public OperationException(int errorCode, string errorDescription) {
ErrorCode = errorCode;
ErrorDescription = errorDescription;
}
}
//The exception throwing way
public class Provider1:IFrProvider {
private readonly IDrvFR48 driver;
public string GetSerialNumber() {
//must read status before get SerialNumber
int resultCode = driver.ReadEcrStatus();
if (resultCode != 0) {
throw new OperationException(resultCode, driver.ErrorDescription);
}
return driver.SerialNumber;
}
}
Call StackException hatanın oluştuğu yere kadar call stack'i gösterir. Debug işlemi için büyük kolaylık sağlar.
Nested Try Catch
Bazen iç içe geçmiş try-catch blokları görebiliyoruz. İlk başta ben de çok saçma olduğunu düşünmüştüm. Ancak daha göze hoş görünmese de sonra bu kullanımın bir yeri olduğunu anladım.
Örnek
Elimizde şöyle bir kod olsun. Hata alınınca işlemi geri sarmak için başka çare olmadığı gösterilmiş.
Exception Anti Pattern'larıtry
{ transaction.commit(); }
catch
{ logerror();
try
{ transaction.rollback(); }
catch
{ seriousLogging(); } }
Control Flow
Örnek - Listenin Sonuna Gelmek
Örneğin veritabanından veri çekerken NoRowsException atılması yanlıştır. Çünkü bir hata alınmamış sadece verinin sonuna gelinmiştir.
Result Object
Bazen exception Result Object yerine kullanılıyor. Açıklaması şöyle.
When sending a request to another module and expecting a result, it seems to me there are two ways of dealing with the 'non-happy paths'.Ancak aslında aranan şeyin bulunamamış olması beklenen bir durum. Bunu ele alabilecek bir Result Object dönmek gerekir. Açıklaması şöyle.
- Throw an exception
- Return a result object that wraps different results (such as value and error)
You have to distinguish between return values and errors.Aslında ben bu hatayı bir kere bir kütüphanede yapmıştım. Aranan şey bulunamadıysa veya girdi hatalı ise hep exception fırlatmıştım. Girdinin hatalı olması durumunda exception kabul edilebilir, ancak aranan şey bulunamadıysa yani işlem başarılı değilse exception fırlatmamak gerekiyordu. Aslında en güzeli Rust dilindeki gibi bir Result tipi tanımlamaktı. Açıklaması şöyle
A return value is one of many possible outcomes of a computation. An error is an unexpected situation which needs to be reported to the caller.
A module may indicate that an error occurred with a special return value or it throws an exception because an error was not expected. That errors occur should be an exception, that's why we call them exceptions.
If a module validates lottery tickets, the outcome may be:
- you have won
- you have not won
- an error occurred (e.g. the ticket is invalid)
In case of an error, the return value is neither "won" nor "not won", since no meaningful statement can be made when e.g. the lottery ticket is not valid.
Result<T, E> is the type used for returning and propagating errors. It is an enum with the variants, Ok(T), representing success and containing a value, and Err(E), representing error and containing an error value.Örnek - Aranan Şeyin Bulunamaması
Elimizde şöyle bir kod olsun. Bu kod parçasında key ile arama yapılıyor, sonuç bulunamazsa lokal exception atılıp akış değiştiriliyor. Ancak exception sadece ne yapacağımızı bilmediğimizde atılmalı.
public String getByKey(String key) {
try {
return getValueByKey(key);
} catch (KeyNotFoundException e) {
String defaultValue = defaultValues.get(key);
valuesFromDatabase.put(key, defaultvalue);
return defaultValue;
}
}
Bu örnekte şöyle yazmak gerekirdi.public String getByKey(String key) {
if (valuesFromDatabase.containsKey(key)) {
return valuesFromDatabase.get(key);
} else {
String defaultValue = defaultValues.get(key);
valuesFromDatabase.put(key, defaultvalue);
return defaultValue;
}
}
Poor Error Handling: Overly Broad ThrowsÖrnek ver
Poor Error Handling: Overly Broad Catch
Örnek ver
Poor Error Handling: Empty Catch Block - Exception'ın Sessizce Yutulması
Açıklaması şöyle.
When writing enterprise software, you will eventually learn an essential truth: the worst bug in the world is not one that causes your program to crash. The worst bug in the world is one which causes your program to silently produce a wrong answer that goes unnoticed but eventually produces a massive negative effect (with severe financial implications for your employer). Thus, error messages and crashes are A Good ThingTM, because they indicate that your program detected a problem.Örnek
Elimizde şöyle bir kod olsun. Bu kodda hata oluştuğunu anlamanın bir yolu yok.
try // If removing one ban fails, eg because of a boneheaded problem,
{ // still try to remove other bans
RemoveBan(ban);
}
catch
{
}
ÖrnekElimizde şöyle bir kod olsun. Burada exception fırlatıldığı halde işlem tamamlanıyor.
try {
// moving funds between accounts
Transaction.start();
accountA.funds += amount;
accountB.funds -= amount; // throws an overflow exception if funds < amount
} catch (Exception ex) {
}
finally {
Transaction.commit();
}
Log and ThrowBu kullanım şeklinde exception sadece loglamak amaçlı olarak yakalanıyor ve tekrar fırlatılıyor. Bu tekniğin aynı exception'ının defalarca loglanmasına yol açacağı açıklanıyor. Benzer bir cevabı burada da görebilirsiniz. Exception-Handling Antipatterns sayfasında bazı yanlış kullanım örnekleri de var.
Hiç yorum yok:
Yorum Gönder