我继承了一个代码库,其中包含如下代码(注意:示例代码是 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
});
此代码对它调用传入函数而不进行异常处理的“测试”环境进行了特殊处理(因此任何异常都保持未处理)。在所有其他环境(例如生产环境)中,它将传入的闭包包装在 try-catch block 中,生产环境的行为与代码最初的行为相同。
这个解决方案解决了我的问题,但它似乎有点老套,让我觉得这不是一个好主意。
我的问题:有什么理由我不应该这样做吗?或者有没有更好的方法来处理这种情况?
请您参考如下方法:
不要试图在异常方面重新发明轮子。您应该在一种情况下catch
一个异常(exception):
捕获异常 如果您有替代计划该怎么办。
异常意味着您的代码遇到了异常情况,在这种情况下它无法继续工作,别无选择,只能认输。这是放弃函数/模块/执行上下文并向更高层的调用者发出信号的一种非常好的方法。这正是异常所做的。
在开发过程中,您希望看到异常的所有丑陋荣耀,以便能够进行调试。在生产中,您不希望您的用户看到异常,而是向他们展示一个漂亮的错误屏幕和/或让各种花里胡哨的声音通知管理员/开发人员/CTO/任何人。
这意味着,在生产中您只需要 一个全局错误处理程序 如果发生意外的、未捕获的异常,它会做出适当的响应。应该像在开发中一样抛出和(不)捕获异常,您不想要两个完全独立的代码路径。这个全局错误处理程序可以通过一些引导脚本有条件地设置,只需使用 set_exception_handler
;或者可能更好,你 配置您的网络服务器适本地提供有用的错误页面。配置 Web 服务器是最好的方法,因为这是系统特定的设置(仅限生产),无需更改任何代码。
唯一一次你应该写一个 try..catch
是如果有一个子系统可能失败的正当原因并且你有一个备用计划。例如。:
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 下载失败是预期结果,可以很好地处理异常,因此这是一个很好的用例。但是,即使它们不代表预期的结果,也不要条件反射地试图捕获它们。