我继承了一个包含如下代码的代码库(注意:示例代码是PHP):
try {
// Do something which doesn't intentionally throw exceptions.
} catch (\Exception $e) {
$this->log->log($e->getMessage());
$this->product->setError($e->getMessage());
return false;
}
基本上,代码正在捕获异常。记录它,以及静默失败(除了日志消息)。
这种行为似乎在生产中有意义,但使开发变得更加困难(因为必须在日志文件中查找堆栈跟踪,而不是将其打印到控制台)。所以我想出了以下功能:
private function tryCatch ($func) {
// Bind closure, so that $this allows it to access class properties
if (is_object($func) && ($func instanceof Closure)) {
\Closure::bind($func, $this, "static");
}
if (\App::environment('test')) {
return $func();
} else {
try {
return $func();
} catch (\Exception $e) {
$this->log->log($e->getMessage());
$this->product->setError($e->getMessage());
return false;
}
}
}
然后可以这样使用:
$this->tryCatch(function () {
// Do something
});
此代码特殊情况下的'test'环境,它调用传入函数而不进行异常处理(因此任何异常都保持未处理状态)。在每个其他环境(例如生产环境)中,它将生成的try-catch块中的传入闭包包装起来,就像代码最初的行为一样。
这个解决方案解决了我的问题,但它看起来有点hacky,让我感到有点唠叨,感觉这不是一个好主意。
我的问题:我有什么理由不这样做吗?或者有更好的方法来处理这种情况吗?
答案 0 :(得分:8)
不要试图重新发明关于异常的轮子。只有一种情况应该catch
例外:
如果您有其他计划如何处理,则抓住例外。
异常意味着您的代码遇到了一个异常情况,它无法继续工作,别无选择,只能放弃。这是放弃函数/模块/执行上下文并向调用者发出更高信号的完美好方法。这正是例外所做的事情。
在开发期间,您希望能够在其所有丑陋的荣耀中看到异常,以便能够进行调试。在生产中,您不希望您的用户看到异常,而是向他们展示一个漂亮的错误屏幕和/或有各种各样的花里胡哨,这通知管理员/开发人员/ CTO /谁。
这意味着,在生产中,您只需要一个全局错误处理程序,如果发生意外的,未捕获的异常,它将作出相应的响应。应该抛出异常并且(不)在开发中完全捕获,您不需要两个完全独立的代码路径。可以通过一些引导脚本有条件地使用set_exception_handler
来设置此全局错误处理程序;或者甚至更好,您适当地配置您的Web服务器以提供有用的错误页面。配置Web服务器是最好的方法,因为这是一个特定于系统的设置(仅限生产),无需更改任何有关代码的内容。
实际编写try..catch
的唯一时间是,如果子系统可能出现故障且您有备份计划的合理原因。 E.g:
try {
$file = download_file_from_url($url);
echo "Cool, got your file.";
} catch (HttpNotFound $e) {
echo "Hey user, that file doesn't exist.";
} catch (HttpEmptyResponse $e) {
echo "Hey user, that file seems empty.";
}
..
在这种情况下,失败的HTTP下载是预期结果,可以很好地处理异常,因此这是一个很好的用例。但是,即使他们没有代表期望的结果,也不要反复尝试抓住所有人。