try catch-blocks를 잘 사용하십니까?


15

나는 항상 이것과 씨름하고 있습니다 ... 시도 잡기와 코드 사이의 올바른 균형을 찾으려고 노력합니다. 탭, 괄호 및 예외의 혼란이 핫 감자처럼 콜백에 던져지지 않습니다. 예를 들어, SQLite를 사용하는 지금 개발중인 앱이 있습니다. SQLite 호출을 추상화하는 데이터베이스 인터페이스와 데이터베이스로 들어가거나 나가는 것을 받아들이는 모델이 있습니다. 따라서 SQLite 예외가 발생하면 모델 (이것이라고 불리는 사람)에게 던져 져야합니다. ), 누가 AddRecord / DeleteRecord / whatever를 호출 한 사람에게 전달해야합니다.  

오류 코드를 무시하거나 잊어 버릴 수 있기 때문에 오류 코드를 반환하는 것과 달리 예외의 팬입니다. 그러나 예외는 본질적으로 처리해야합니다 (허가, 즉시 잡을 수 있습니다 ...). 제가 지금 진행하고있는 것보다 더 나은 방법이 있어야합니다.

편집 :  나는 이것을 조금 다르게 표현해야합니다. 나는 다른 유형으로 다시 던지는 것을 이해합니다. 내 질문은 ... 코드를 깨끗하게 유지하는 가장 좋은 방법은 무엇입니까? 그것은 얼마 후 나에게 매우 혼란스럽게 느끼기 시작합니다.


어떤 프로그래밍 언어?
Apalala

2
현재 C #이지만 일반적으로 생각하려고합니다.
trycatch

C #에서는 예외 선언을 강제로 수행하지 않으므로 합리적인 경우 예외를보다 쉽게 ​​처리 할 수 ​​있으며 프로그래머가 실제로 처리하지 않고 예외를 포착하려는 유혹을 피할 수 있습니다. C #의 디자이너 인 Anders Hejlsberg는이 기사 artima.com/intv/handcuffs.html
Apalala

답변:


14

강력한 유형의 언어를 사용하지 않더라도 강력한 타이핑의 관점에서 생각하십시오. 메소드 예상 한 유형을 리턴 할 수 없으면 예외가 발생해야합니다.

또한 SQLException을 모델 (또는 더 나쁜 경우 UI)까지 내 던지기보다는 각 계층에서 알려진 예외를 포착하고 해당 계층에 적합한 예외로 랩 / 돌연변이 / 교체해야합니다.

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

이를 통해 각 계층에서 찾고있는 예외 수를 제한하고 체계적인 예외 시스템을 유지 관리 할 수 ​​있습니다.


몇 가지 수정 작업을 수행하여 원래 Q를 잘못 작성했습니다. 내 질문은 모든 시도와 잡기 등을 처리하면서 코드를 깨끗하게 유지하는 방법에 대한 것입니다. try catch 블록이 어디에나있을 때 매우 혼란
스러워

1
@Ed는 UI 또는 모델이 "SQLException"을 포착한다고 귀찮게하지 않습니까? 나에게는 그다지 관련이 없습니다. 그들 각자에게, 나는 추측한다.
Nicole

1
@Ed, 예외를 확인하지 않은 언어에서는 괜찮을 수도 있지만 예외를 확인한 언어 throws SQLException에서는 SQL이 관련되어 있음을 암시하지 않는 방법을 사용하는 것이 실제로 추악합니다 . 일부 작업이 파일 저장소로 이동하기로 결정하면 어떻게됩니까? 이제는 throws SQLException, IOException등 을 선언해야합니다 . 손을 get 수 있습니다.
Mike Daniels

1
@Ed 최종 사용자에게 SQLException은 그다지 의미가 없으며, 특히 시스템이 관계형 데이터베이스 외에 여러 유형의 지속성을 지원할 수있는 경우에 그러합니다. GUI 프로그래머로서 전체 하위 수준 예외보다 DataNotFoundException을 처리해야합니다. 저수준 라이브러리에 대한 예외는 종종 정상 수명의 일부일뿐입니다.이 경우 추상화 수준이 응용 프로그램과 일치 할 때 처리하기가 훨씬 쉽습니다.
Newtopian

1
@Newtopian-나는 최종 사용자에게 원시 예외를 제시한다고 말한 적이 없다. 최종 사용자에게는 간단한 '프로그램이 작동을 멈췄습니다'라는 메시지만으로 충분합니다. 디버그 모드에서는 예외를 표시하는 것이 유용합니다. 이러한 예외에 대한 특정 처리기가없는 경우 API에서 발생할 수있는 모든 예외를 신경 쓰지 않아야합니다. 처리되지 않은 예외 캐처가 모든 것을 처리하도록하십시오.
Ed James

9

대부분의 경우 정상적인 경우를 처리하고 예외적 인 경우는 다른 상황에서도 처리 할 수 ​​있기 때문에 예외로 인해보다 명확한 코드를 작성할 수 있습니다.

예외 처리 (캐치) 규칙은 실제로 무언가를 수행 할 수있는 컨텍스트에 의해 수행되어야한다는 것입니다. 그러나 그것은 예외가 있습니다.

예외는 모듈 경계 (특히 계층 경계)에서 발생해야하며,이를 둘러싸더라도 호출자에게 의미가있는 상위 수준 예외를 발생시킵니다. 각 모듈과 계층은 예외와 관련하여 구현 세부 사항을 숨겨야합니다 (힙 모듈은 HeapFull을 발생 시키지만 ArrayIndexOutOfBounds는 절대로 발생하지 않습니다).

귀하의 예에서 상위 계층이 SQLite 예외에 대해 아무것도 할 수 없을 것입니다 (그렇다면 SQLite에 너무 결합되어 데이터 계층을 다른 것으로 전환 할 수 없음). 추가 / 삭제 / 업데이트와 같은 것들이 실패 할 것으로 예상 할 수있는 몇 가지 이유가 있으며, 그 중 일부 (동시 트랜잭션의 호환되지 않는 변경 사항)는 데이터 / 지속성 계층 (무결성 규칙 위반, fi)에서도 복구가 불가능합니다. 지속성 계층은 예외를 모델 계층 용어에서 의미있는 것으로 변환하여 상위 계층이 재 시도 할 것인지 또는 정상적으로 실패 할지를 결정할 수 있습니다.


나는 내가 잘못 말한 곳을 반영하기 위해 내 질문에 동의하고 편집했다. 나는 이것이 노력하고 어수선한 것들을 혼란스럽게 만드는 방법에 대한 더 많은 질문이 되길 의미했다.
trycatch

@Ed James의 원칙을 적용 할 수 있으며 레이어 또는 모듈 에서 언급했습니다 . 모든 곳에서 SQLite를 직접 호출하는 대신 SQLite와 대화하고 예외를 복구하거나보다 일반적인 것으로 변환하는 몇 가지 메소드 / 함수가 있습니다. 트랜잭션에 여러 쿼리와 업데이트가 포함 된 경우 각 가능한 모든 예외를 처리 할 필요가 없습니다. 단일 외부 try-catch가 예외를 변환하고 내부 트랜잭션이 롤백을 통해 부분 업데이트를 처리 할 수 ​​있습니다. 간단한 예외 처리를 위해 업데이트를 자체 함수로 이동할 수도 있습니다.
Apalala

1
이 예제는 코드 예제로 이해하기가 더 쉬울 것입니다.
Click Upvote

이것은 아마도 처음부터 스택 오버 플로우에 더 적합한 질문 일 것입니다.
Apalala

5

일반적으로 특정 예외 (예 : IOException) 만 포착해야하며 예외를 발견 한 후 수행해야 할 특정 조치가있는 경우에만 해당됩니다.

그렇지 않으면, 예외를 노출시켜 처리하고 처리 할 수 ​​있도록하는 것이 가장 좋습니다. 어떤 사람들은 이것을 빠른 실패라고 부릅니다.

아래에서 버블 링 된 처리되지 않은 예외를 포착하려면 응용 프로그램 루트에 일종의 처리기가 있어야합니다. 이를 통해 예외를 적절한 방식으로 제시,보고 또는 관리 할 수 ​​있습니다.

예외 랩핑은 분산 시스템에서 예외를 발생시켜야하고 클라이언트에 서버 측 결함에 대한 정의가없는 경우에 유용합니다.


예외 잡기 및 침묵은 끔찍한 일입니다. 프로그램이 중단 된 경우 예외 및 추적으로 인해 충돌이 발생합니다. 데이터를 조용히 기록하지만 데이터를 엉망으로 만드는 데 방해가됩니다.
S.Lott

1
@ S.Lott 나는 그들이 성가시다는 것을 알기 때문에 예외를 침묵시키지 말아야하지만 응용 프로그램을 충돌시키는 것은 약간 극단적이라는 것에 동의합니다. 예외를 포착하고 알려진 상태에서 시스템을 재설정 할 수있는 경우가 많이 있으며,이 경우 모든 것의 전역 처리기가 매우 유용합니다. 그러나 재설정 프로세스가 안전하게 처리 할 수없는 방식으로 실패하면 예, 머리가 모래에 붙어있는 것보다 충돌이 훨씬 낫습니다.
Newtopian April

2
다시 말하지만 그것은 모두 당신이 짓고있는 것에 달려 있지만 추상화 누출을 일으키기 때문에 거품이 생기는 것에 동의하지 않습니다. API를 사용할 때 API 모델과 일치하는 예외가 노출되는 것이 훨씬 낫습니다. 나는 또한 놀라움을 싫어하기 때문에 API가 예외와 관련하여 예외없이 구현과 관련된 예외를 알면 싫어. 무슨 일이 생길지 모른다면 어떻게 반응 할 수 있을까요! 놀라움으로 인해 내 앱이 예기치 않게 충돌하는 것을 막기 위해 훨씬 더 넓은 캐치 넷을 자주 사용한다는 것을 알았습니다.
Newtopian

@Newtopian : "래핑"예외 및 "다시 쓰기"는 추상화의 누출을 줄입니다. 그들은 여전히 ​​적절하게 거품을 일으킨다. "내가 무슨 일이 일어날 지 전혀 모른다면 어떻게 반응 할 수 있을까! 너무 넓은 캐치 그물을 자주 사용한다는 것을 알게되었습니다." 모든 것을 잡을 필요는 없습니다. 시간의 80 %, 옳은 것은 아무것도 잡을 수 없습니다. 20 %의 시간이 의미있는 응답이 있습니다.
S.Lott

1
@Newtopian, 우리는 일반적으로 객체에 의해 발생되어 랩핑되어야하는 예외와 객체 코드의 버그로 인해 발생하여 랩핑되어서는 안되는 예외를 구별해야한다고 생각합니다.
Winston Ewert

4

스택 클래스를 작성한다고 가정하십시오. 클래스에 예외 처리 코드를 넣지 않으면 다음과 같은 예외가 발생할 수 있습니다.

  1. ArrayIndexError-사용자가 빈 스택에서 팝을 시도 할 때 발생합니다.
  2. NullPtrException-구현의 버그로 인해 null 참조를 참조하려고했기 때문에 발생

예외 래핑에 대한 간단한 접근 방식은 이러한 두 예외를 모두 StackError 예외 클래스로 래핑하기로 결정할 수 있습니다. 그러나 이것은 예외를 래핑하는 요점을 실제로 놓칩니다. 개체가 하위 수준 예외를 throw하면 개체가 손상되었음을 의미합니다. 그러나 이것이 허용되는 한 가지 경우가 있습니다 : 객체가 실제로 파손 된 경우.

예외를 줄 바꿈하는 것은 개체가 정상적인 오류에 대해 적절한 예외를 제공해야한다는 것입니다. 빈 스택에서 팝할 때 스택은 ArrayIndexError가 아닌 StackEmpty를 발생시켜야합니다. 의도는 없는 개체 또는 코드가 파손 된 경우 다른 예외가 발생하지 않도록 할 수 있습니다.

우리가 정말로 피하고 싶은 것은 높은 수준의 객체를 통해 전달 된 낮은 수준의 예외를 포착하는 것입니다. 빈 스택에서 팝할 때 ArrayIndexError를 발생시키는 스택 클래스는 사소한 문제입니다. 실제로 해당 ArrayIndexError를 잡으면 심각한 문제가 있습니다. 낮은 수준의 오류를 전파하는 것은 훨씬 덜 심각한 죄입니다.

이것을 SQLException 예제로 다시 가져 오려면 왜 SQLException이 발생합니까? 한 가지 이유는 잘못된 쿼리를 전달하기 때문입니다. 그러나 데이터 액세스 계층이 잘못된 쿼리를 생성하는 경우 해당 쿼리가 손상됩니다. DataAccessFailure 예외에서 끊어진 부분을 다시 줄이려고 시도해서는 안됩니다.

그러나 데이터베이스 연결이 끊어져서 SQLException이 발생할 수도 있습니다. 그 시점에 대한 나의 전략은 마지막 방어 라인에서 예외를 포착하고 데이터베이스 연결이 끊어지고 종료되었다는 것을 사용자에게보고하는 것입니다. 응용 프로그램이 데이터베이스에 액세스 할 수 없으므로 실제로 수행 할 수있는 작업이 많지 않습니다.

코드가 어떻게 생겼는지 모르겠습니다. 그러나 모든 예외를 맹목적으로 더 높은 수준의 예외로 변환하는 것처럼 들립니다. 비교적 적은 수의 경우에만 그렇게해야합니다. 가장 낮은 수준의 예외는 코드의 버그를 나타냅니다. 잡기와 다시 포장하는 것은 비생산적입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.