Восстановление исключения только потому, что вы решили записать его с помощью блока catch (то есть исключение не изменилось вообще), является плохой идеей.
Одна из причин, по которой мы используем исключения, сообщения об исключениях и их обработку, заключается в том, что мы знаем, что пошло не так, и грамотно написанные исключения могут значительно ускорить поиск ошибки.
Также помните, что обработка исключений требует гораздо больше ресурсов, чем, скажем, есть if
, поэтому вам не следует часто их обрабатывать только потому, что вам так хочется. Это влияет на производительность вашего приложения.
Тем не менее, рекомендуется использовать исключение в качестве средства для обозначения прикладного уровня, на котором возникла ошибка.
Рассмотрим следующий полупсевдокод:
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
}
}
Давайте предположим, что кто-то вызвал GetNumberAndReturnCode
и получил 500
код, сигнализирующий об ошибке. Он позвонит в службу поддержки, который откроет файл журнала и увидит это:
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
Затем разработчик сразу узнает, какой уровень программного обеспечения вызвал прерывание процесса, и может легко определить проблему. В этом случае это критично, потому что тайм-аут Redis никогда не должен произойти.
Возможно, другой пользователь вызовет тот же метод, также 500
получит код, но в журнале будет показано следующее:
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.
В этом случае служба поддержки может просто ответить пользователю, что запрос недействителен, потому что он запрашивает значение для несуществующего идентификатора.
Резюме
Если вы обрабатываете исключения, убедитесь, что обрабатываете их правильно. Также убедитесь, что ваши исключения в первую очередь содержат правильные данные / сообщения, следуя уровням вашей архитектуры, чтобы сообщения помогли вам определить проблему, которая может возникнуть.