Ponowne sprawdzenie wyjątku tylko dlatego, że zdecydowałeś się go zarejestrować za pomocą bloku catch (co oznacza, że wyjątek wcale się nie zmienił) jest złym pomysłem.
Jednym z powodów, dla których używamy wyjątków, komunikatów o wyjątkach i ich obsługi jest to, abyśmy wiedzieli, co poszło nie tak i sprytnie napisane wyjątki mogą znacznie przyspieszyć znalezienie błędu.
Pamiętaj też, że obsługa wyjątków kosztuje znacznie więcej zasobów niż, powiedzmy, mieć if
, więc nie powinieneś zajmować się nimi zbyt często tylko dlatego, że masz na to ochotę. Ma to wpływ na wydajność Twojej aplikacji.
Jednak dobrym rozwiązaniem jest użycie wyjątku jako środka do zaznaczenia warstwy aplikacji, w której wystąpił błąd.
Rozważ następujący półpseudo kod:
interface ICache<T, U>
{
T GetValueByKey(U key); // may throw an CacheException
}
class FileCache<T, U> : ICache<T, U>
{
T GetValueByKey(U key)
{
throw new CacheException("Could not retrieve object from FileCache::getvalueByKey. The File could not be opened. Key: " + key);
}
}
class RedisCache<T, U> : ICache<T, U>
{
T GetValueByKey(U key)
{
throw new CacheException("Could not retrieve object from RedisCache::getvalueByKey. Failed connecting to Redis server. Redis server timed out. Key: " + key);
}
}
class CacheableInt
{
ICache<int, int> cache;
ILogger logger;
public CacheableInt(ICache<int, int> cache, ILogger logger)
{
this.cache = cache;
this.logger = logger;
}
public int GetNumber(int key) // may throw service exception
{
int result;
try {
result = this.cache.GetValueByKey(key);
} catch (Exception e) {
this.logger.Error(e);
throw new ServiceException("CacheableInt::GetNumber failed, because the cache layer could not respond to request. Key: " + key);
}
return result;
}
}
class CacheableIntService
{
CacheableInt cacheableInt;
ILogger logger;
CacheableInt(CacheableInt cacheableInt, ILogger logger)
{
this.cacheableInt = cacheableInt;
this.logger = logger;
}
int GetNumberAndReturnCode(int key)
{
int number;
try {
number = this.cacheableInt.GetNumber(key);
} catch (Exception e) {
this.logger.Error(e);
return 500; // error code
}
return 200; // ok code
}
}
Załóżmy, że ktoś zadzwonił GetNumberAndReturnCode
i odebrał 500
kod, sygnalizując błąd. Zadzwoni do pomocy technicznej, która otworzy plik dziennika i zobaczy to:
ERROR: 12:23:27 - Could not retrieve object from RedisCache::getvalueByKey. Failed connecting to Redis server. Redis server timed out. Key: 28
ERROR: 12:23:27 - CacheableInt::GetNumber failed, because the cache layer could not respond to request. Key: 28
Następnie programista natychmiast wie, która warstwa oprogramowania spowodowała przerwanie procesu, i ma łatwy sposób na zidentyfikowanie problemu. W tym przypadku jest to krytyczne, ponieważ przekroczenie limitu czasu Redis nigdy nie powinno się zdarzyć.
Być może inny użytkownik wywołałby tę samą metodę, również otrzymał 500
kod, ale dziennik pokazałby:
INFO: 11:11:11- Could not retrieve object from RedisCache::getvalueByKey. Value does not exist for the key 28.
INFO: 11:11:11- CacheableInt::GetNumber failed, because the cache layer could not find any data for the key 28.
W takim przypadku obsługa może po prostu odpowiedzieć użytkownikowi, że żądanie jest nieprawidłowe, ponieważ żąda wartości dla nieistniejącego identyfikatora.
streszczenie
Jeśli zajmujesz się wyjątkami, upewnij się, że obchodzisz się z nimi we właściwy sposób. Upewnij się także, że Twoje wyjątki obejmują przede wszystkim prawidłowe dane / komunikaty, zgodnie z warstwami architektury, aby komunikaty pomogły Ci zidentyfikować problem, który może wystąpić.