这是一段代码,我想知道是否应该重构它以使其更符合清洁代码实践。
这是一个负责退还客户订单的课程。
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
$this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
foreach ($this->_orders as $order) {
try {
$order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
$this->updateOrderStatus('refunded')
} catch (Exception $e) {
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
}
当然这段代码比原始代码高度简化。
我的主要问题是,如果$order->refund();
抛出异常,它将被捕获并记录到数据库,然后发送邮件。但是,如果$this->logError($e);
本身抛出异常怎么办?或者如果邮件服务器关闭并抛出异常怎么办?
如果数据库失效并且$this->getOrdersFromDB();
抛出异常会更好吗?
我的第一个解决方案是将所有内容包装在一个大的try{}catch{}
:
public function refundOrder() {
try {
$this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
foreach ($this->_orders as $order) {
$order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
$this->updateOrderStatus('refunded')
} catch (Exception $e) {
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
但这意味着如果一个订单失败那么全部都会失败!我应该为整个功能设置2 try{}catch{}
,为每个订单设置另一个吗?但在这种情况下,catch中的函数也可能抛出一个不会被捕获的异常。
注意:
应用程序是使用Zend framework 1.11.11构建的。
提前致谢。
答案 0 :(得分:3)
解决此类问题没有灵丹妙药。如果一个函数可以抛出而你关心它,你必须在它周围包裹try
/ catch
- 就这么简单。
进入细节:在没有关于应用程序架构的更多信息的情况下,不可能评估这种或那种方法的优点,但这里有一些一般性的建议:
refundOrder
。确保订单已成功加载;确保您要操作的订单全部可退款(尝试退还因业务逻辑而无法退款的订单的目的是什么?不应通知用户/运营商在尝试退款之前?)。try
/ catch
级别,但可能并非全部位于refundOrder
内。外部块用于捕获真正意外的任何错误,那么仅在refundOrder
内捕获它们有什么意义呢?你也不想在app的其他部分那样做吗?最里面的try
/ catch
是必要的,这样一个不可退款的订单就不会杀死所有流程。答案 1 :(得分:1)
如果您需要记录并邮寄每个例外,那么您需要这样的事情:
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
try {
$this->getOrdersFromDB();
foreach ($this->_orders as $order) {
try {
$order->refund();
} catch (MyBusinessException $e) {
# deals with the problematic $order without stopping the loop
$this->logError($e);
$this->sendMailToAdmin();
}
$this->updateOrderStatus('refunded');
}
} catch(Exception $e) {
# deals with unexpected bugs
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
但是你必须在日志和邮件方法中放置一个try / catch,以防止它们在服务器脱机时抛出任何异常。如果不这样做,则在日志/邮件失败时将停止$ orders循环。
如果您需要在refund()方法中仅记录/邮寄您的业务例外,那么您需要这样的事情:
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
$this->getOrdersFromDB();
foreach ($this->_orders as $order) {
try {
$order->refund();
} catch (MyBusinessException $e) {
# deals with the problematic $order without stopping the loop
$this->logError($e);
$this->sendMailToAdmin();
}
$this->updateOrderStatus('refunded');
}
}
}
任何其他异常都会导致http 500 - 内部服务器错误页面,这通常是您想要的,因为它是一个意外的错误。
还有其他方法可以解决这个问题,但正如您所见,这取决于您的需求。
答案 2 :(得分:0)
由于logError($e)
和sendMailToAdmin()
可能是最后使用的函数,通常在try / catch块中,我会确保它们永远不会抛出异常。
function logError($e)
{
try
{
//your logic that may throw an Exception here
}
catch {}
}
function sendMailToAdmin($e)
{
try
{
//your logic that may throw an Exception here
}
catch {}
}