2012-04-18 3 views
1

이 코드는 Clean Code 사례를보다 잘 준수하도록 리팩토링해야하는지 궁금해하고있는 부분입니다.catch 문에서 함수에 대한 오류 처리

고객이 주문한 일부 주문에 대한 환불을 담당하는 클래스입니다.

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();이 예외를 throw하면 DB에 걸려서 로그온되어 메일이 전송된다는 것입니다. 그러나 $this->logError($e); 자체가 예외를 throw하면 어떻게됩니까? 또는 메일 서버가 다운되어 예외가 발생하면 어떻게해야합니까?

DB가 다운되어 $this->getOrdersFromDB();이 예외를 throw하면 어떻게 될까요?

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(); 
       } 
      } 
     } 

그러나 하나 개의 순서는 모든 실패 실패 할 경우 그 의미! :

나의 첫 번째 솔루션은 하나의 큰 try{}catch{} 모든 것을 포장했다 전체 기능에 대해 2 개의 try{}catch{}을 넣어야하고 각 주문에 대해 다른 하나를 넣어야합니까? 그러나이 경우에도 catch의 함수가 catch되지 않는 예외를 throw 할 수 있습니다.

참고 :

응용 프로그램이 젠드 프레임 워크 1.11.11를 사용하여 구축된다.

미리 감사드립니다.

+0

'$ this-> logError()'는 실제로 예외를 던집니까? 그게 모범 사례가 아닌 것처럼 보입니다. 무한 루프에 쉽게 끝날 수 있습니다. 오류 로깅/관리자 전자 메일 전송에 실패하면 응용 프로그램에서 수행 할 작업을 결정해야합니다. 오류를 무시하고 계속 진행하면 해당 메소드의 예외를 throw하지 마십시오. 스택 트레이스를 가지고 죽고 싶다면, 예외를 잡아서는 안된다. 당신이 그 상황을 어떻게 다루는 지 알고 싶다면, 내부 try/catch를 사용하십시오. IMHO ... – DaveRandom

+0

기본적으로 yeh 또는 위의 코드의 'catch'블록 안에 -'try {throw new Exception(); } catch (Exception $ e) {try {handle_exception ($ e); } catch (Exception $ e2) {}}' – DaveRandom

답변

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(); 
     } 
    } 
} 

을하지만 당신은 던질 것을 방지하기 위해 로그 및 메일 방법 내부의 시도/캐치를 넣어야 할 서버가 오프라인 일 때의 예외입니다. 그렇지 않으면 로그/메일 실패시 $ orders 루프가 중지됩니다. 내부 서버 오류 페이지 -

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으로 이어질 것입니다 :() 메서드 환불에/메일 만 비즈니스 예외를 기록해야하는 경우

는, 당신은이 같은 뭔가가 필요 이는 예상치 못한 버그이기 때문에 보통 원하는 것입니다.

다른 방법으로 처리 할 수 ​​있지만, 사용자의 필요에 따라 다릅니다.

+1

@Jon이 말한 것에도주의를 기울여야합니다. 왜냐하면 전체 시스템에 대해 로그/메일 예외를 필요로 할 경우 refundOrder에서 작성한 가장 바깥 쪽의 try/catch는 프런트 컨트롤러에 클래스 외부에 배치해야하기 때문입니다. –

+0

내 컨트롤러에서'RefundServiceInvoker' 클래스의 인스턴스를 생성 한 다음'try/catch' 메서드를 컨트롤러에 넣어야 만'refundOrder()'를 호출 할 수 있습니까? – Songo

+1

만약 당신이 혼자서 전체 시스템을 작성했다면, 모든 HTTP 요청을 처리하는 FRONT 컨트롤러 안에 가장 바깥 쪽 try/catch를 두어야한다고 생각합니다. 이렇게하면 예기치 않은 버그를 처리하기 위해 try/catch를 하나만 사용할 수 있습니다. 그러나 Zend Framework를 사용하고있는 것을보고 있으므로 예기치 않은 예외를 잡아서 원하는 작업 (로그/메일/etc)을 수행 할 수있는 오류 처리기를 올바르게 등록하는 방법을 알아 보려면 문서를 살펴 봐야합니다. http://framework.zend.com/manual/en/zend.controller.exceptions.html –

3

이러한 문제를 해결할 마법의 탄환이 없습니다. 함수가 던져서 신경 쓸 수 있다면, 그 주위에 try/catch을 감싸 야합니다. 그것은 당신의 응용 프로그램의 아키텍처에 대한 더 많은 정보를 필요없이 이런 저런 방식의 장점을 평가하기 위해 정말 불가능하지만, 여기에 몇 가지 일반적인 제안은 다음과 같습니다 :

는 세부로 이동하려면

  • 하는를 수행은 전제 조건을 확인한 후에도 refundOrder으로 전화하십시오. 주문이 성공적으로로드되었는지 확인하십시오. 운영 예정인 주문이 환불 가능 (비즈니스 로직으로 인해 환불되지 않는 주문을 환불하려는 목적은 사용자/운영자에게 환불 전에 통지해서는 안됩니다. 시도됩니까?).
  • try/catch의 두 가지 이상의 레벨을 사용하지만 모두 내부가 아님 refundOrder입니다. 바깥 쪽 블록은 예기치 않은 이라는 오류를 잡으려고 의도 한 것이므로 refundOrder 안에있는 오류를 잡기위한 요점은 무엇입니까? 당신도 애플 리케이션의 다른 부분에서 그렇게하고 싶지 않아? 가장 안쪽 인 try/catch은 환불되지 않는 주문으로 인해 모든 절차가 중단되지 않도록해야합니다.
+0

내 응용 프로그램이 Zend 프레임 워크를 사용하여 작성되었다는 것을 잊어 버렸습니다. 내 컨트롤러 안에'RefundServiceInvoker' 클래스의 인스턴스를 생성 한 다음'try/catch'를 컨트롤러에 넣어야 만'refundOrder()'를 호출할까요? – Songo

0
logError($e)으로

sendMailToAdmin() 아마 일반적으로 시도/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 {} 
} 
+0

예외를 삼키는 것이 좋지 않기 때문에 "catch"문을 비워 두지 마십시오. 오류가 발생하는 시점을 알 수 없습니다. –

+0

@ André : 나는 예외를 삼키는 것이 일반적으로 좋은 습관이 아니라는 데 동의하지만, 당신이 대답 할 때 "당신은 예외를 던지지 않도록 로그/메일 메서드 안에 try/catch를 넣어야 만한다"고 동의합니다. 그렇다면이 catch 블록에 무엇을 넣으시겠습니까? – nIcO

+0

예를 들어 경고를 더 잘 제기하는 것이 좋습니다. 따라서 웹 서버 로그 파일에서 사용할 수 있습니다 : catch (Exception $ e) {trigger_error ($ e-> getMessage(), E_USER_WARNING); } –

관련 문제