Rialzare un'eccezione solo perché hai deciso di registrarla utilizzando un blocco catch (ovvero l'eccezione non è stata affatto modificata) è una cattiva idea.
Uno dei motivi per cui utilizziamo eccezioni, messaggi di eccezioni e la sua gestione è che sappiamo cosa è andato storto e le eccezioni scritte in modo intelligente possono velocizzare il rilevamento del bug con un ampio margine.
Ricorda inoltre che la gestione delle eccezioni costa più risorse di quanto si supponga abbiano if
, quindi non dovresti gestirle tutte spesso solo perché ne hai voglia. Ha un impatto sulle prestazioni della tua applicazione.
È comunque un buon approccio usare l'eccezione come mezzo per contrassegnare il livello dell'applicazione in cui è apparso l'errore.
Considera il seguente codice semi-pseudo:
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
}
}
Supponiamo che qualcuno abbia chiamato GetNumberAndReturnCode
e abbia ricevuto il codice 500
, segnalando un errore. Chiamerebbe il supporto, chi aprirà il file di registro e vedrà questo:
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
Lo sviluppatore quindi sa immediatamente quale livello del software ha causato l'interruzione del processo e ha un modo semplice per identificare il problema. In questo caso è fondamentale, perché il timeout di Redis non dovrebbe mai accadere.
Forse un altro utente chiamerebbe lo stesso metodo, riceverà anche il codice 500
, ma il registro mostrerebbe quanto segue:
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.
In questo caso il supporto potrebbe semplicemente rispondere all'utente che la richiesta non è valida perché sta richiedendo un valore per un ID inesistente.
Sommario
Se gestisci le eccezioni, assicurati di gestirle nel modo corretto. Assicurati inoltre che le eccezioni includano i dati / i messaggi corretti in primo luogo, seguendo i livelli dell'architettura, in modo che i messaggi ti aiutino a identificare un problema che potrebbe verificarsi.