언제 예외를 던지나요?


435

내 응용 프로그램이 기대하지 않는 모든 조건에 대해 예외가 생성되었습니다. UserNameNotValidException, PasswordNotCorrectException

그러나 나는 그러한 조건에 대해 예외를 만들지 말아야한다고 들었습니다. 내 UML에서 주 흐름에 대한 예외는 예외이므로 왜 예외가 아니어야합니까?

예외를 만드는 지침이나 모범 사례가 있습니까?


58
다시 시작하십시오. 이것은 매우 현명하고 유효한 질문입니다. 모든 질문에는 일정량의 의견이 수반되지만이 경우 '모범 사례'의 문제라고 생각합니다.
Tim Long

19
다시 열면 +1입니다. 다른 많은 흥미로운 주제가 '의존'하고 의사 결정을 할 때 장단점을 분석하는 것이 매우 유용합니다. 사람들이 의견에 대한 답변을 사실과 혼동한다고해서 이것이 부정되는 것은 아닙니다. 진흙을 걸러내는 것은 독자에게 맡겨야하는 운동입니다.
aron

12
또한이 질문이 모범 사례와 관련되어 있으므로 다시 열어야한다는 데 동의합니다. 그런데 모범 사례는 항상 다른 사람들을 도울 수있는 의견입니다.
Ajay Sharma

6
"오류 코드를 반환하지 마십시오. 예외는 프레임 워크에서 오류를보고하는 주요 수단입니다." "... 멤버가 의도 한 작업을 성공적으로 수행 할 수 없으면 실행 실패로 간주되고 예외가 발생해야합니다." msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
이것들은 완전히 현명한 예외 일 수 있습니다. 어떻게 던지는 메소드에 달려 있습니다. IsCredentialsValid(username,password)사용자 이름 또는 비밀번호가 유효하지 않은 경우 호출 된 메소드 는 예외를 발생시키지 말고 false를 리턴해야합니다. 그러나 인증이 실패하면 데이터베이스에서 데이터를 읽는 방법이 합법적으로 그러한 예외를 던질 수 있다고 말하십시오. 간단히 말해서 : 메소드가 수행해야 할 작업을 수행 할 수없는 경우 예외를 throw해야합니다.
JacquesB

답변:


629

내 개인적인 지침은 : 현재 코드 블록의 기본 가정이 잘못된 것으로 판명되면 예외가 발생합니다.

예제 1 : 임의의 클래스를 검사하고 해당 클래스가 List <>에서 상속되면 true를 반환하는 함수가 있다고 가정하십시오. 이 함수는 "이 개체는 List의 자손입니까?"라는 질문을합니다. 이 함수는 연산에 회색 영역이 없기 때문에 예외를 발생시키지 않아야합니다. 모든 단일 클래스는 List <>에서 상속되거나 상속되지 않으므로 항상 "yes"또는 "no"입니다.

예제 2 : List <>를 검사하고 길이가 50보다 크면 true를 반환하고 길이가 작 으면 false를 반환하는 다른 함수가 있다고 가정 해보십시오. 이 함수는 "이 목록에 50 개가 넘는 항목이 있습니까?"라는 질문을합니다. 그러나이 질문은 가정합니다. 주어진 대상이 목록이라고 가정합니다. 내가 NULL을 건네면 그 가정은 거짓입니다. 이 경우 함수가 true 또는 false를 반환 하면 자체 규칙을 위반하는 것입니다. 이 함수는 아무 것도 반환 할 수 없으며 질문에 올바르게 답변했다고 주장 할 수 없습니다 . 따라서 반환되지 않습니다-예외가 발생합니다.

이것은 "로드 된 질문" 논리적 오류 와 비슷합니다 . 모든 기능은 질문을합니다. 입력이 주어지면 그 질문에 오류가 생기면 예외를 던집니다. 이 줄은 void를 반환하는 함수를 사용하여 그리기가 더 어렵지만 결론은 입력에 대한 함수의 가정이 위반되면 정상적으로 반환하는 대신 예외를 발생시키는 것입니다.

이 방정식의 다른 측면은 다음과 같습니다. 함수에서 예외를 자주 발생시키는 경우 가정을 구체화해야합니다.


15
바로 그거죠! 함수 전제 조건 (인수에 대한 가정) 이 깨졌을 때만 예외가 발생합니다 !
라이트 만

11
언어학에서는이를 전제 실패 라고도 합니다. 전형적인 예는 베르트랑 러셀 (Bertrand Russell) 때문입니다. "프랑스의 대머리입니까?"는 예 또는 아니오로 대답 할 수 없습니다. 즉, 프랑스 왕이 있다는 것입니다. 전제 실패는 종종 명확한 설명으로 볼 수 있으며 프로그래밍 할 때 일반적입니다. 예를 들어 "목록의 헤드"는 목록이 비어있을 때 전제 실패를 겪고 예외를 던지는 것이 적절합니다.
모한

이것은 아마도 가장 좋은 설명 일 것입니다!
gaurav

감사합니다. 그래서. 많은. "프랑스의 대머리 야" 나는 meinong의 정글을 연구 할 때 이것을 들었다 .... :) 감사합니다. @Mohan
ErlichBachman

285

그것들은 정상적으로 일어날 일이기 때문입니다. 제어 흐름 메커니즘은 예외입니다. 사용자는 종종 암호를 잘못받습니다. 예외가 아닙니다. 예외는 정말 드물고 UserHasDiedAtKeyboard유형 상황 이어야합니다 .


3
흠. 최대 성능이 필요하지 않은 경우 제어 흐름 메커니즘으로 예외를 사용할 수 있습니다. 이는 대부분의 웹앱에 해당됩니다. 파이썬은 예외 'StopIteration'을 사용하여 반복자를 종료하며 매우 잘 작동합니다. 비용은 IO 등에 비하면 아무것도 아닙니다.
Seun Osewa

9
탁월한 답변 +1. 나는 API를 사용하는 개발자들에 대해 너무 실망하여 소비해야하며 모든 작은 일에 대해 예외를 던집니다. 매우 예외적 인 경우가 거의 없습니다. 25 가지 종류의 예외가 정의되어 있으면 디자인을 다시 한 번 살펴보십시오. 잘못했을 수 있습니다.
7wp

1
사용자가 웹 페이지 코드를 조작하여 허용되지 않는 불법적 인 행동을 시도 할 때 예외가 발생해야합니다 (예 : StackOverflow에서 다른 사람의 게시물 삭제).
라자 트 굽타

30
제어 흐름 메커니즘은 예외입니다. 던질 수 있습니다. 당신은 그들을 잡을 수 있습니다. 컨트롤을 다른 코드 조각으로 옮겼습니다. 그것은 제어 흐름입니다. 언어에 예외가있는 유일한 이유는 "실제로 실패 했습니까?"라고 묻지 않고 간단한 코드를 작성할 수 있기 때문입니다. 당신이하는 모든 후. 예를 들어, 하스켈에는 모나드와 do-notation이 오류 검사를 자동화 할 수 있기 때문에 예외가 없습니다.
Jesse

2
예외는 제어 흐름 메커니즘 이상입니다. 이들은 (메소드) 고객에게 알고 처리 해야하는 예외적 인 결과에 대한 유용한 정보를 제공합니다 . 즉, 제대로 사용하면 예외가 API를 더욱 강력하게 만듭니다
idelvall

67

저의 작은 가이드 라인은 "Code complete"라는 훌륭한 책에 크게 영향을받습니다.

  • 무시해서는 안되는 사항에 대해 알리려면 예외를 사용하십시오.
  • 오류를 로컬에서 처리 할 수있는 경우 예외를 사용하지 마십시오
  • 예외가 나머지 루틴과 동일한 추상화 레벨에 있는지 확인하십시오.
  • 예외적 인 사항은 예외 입니다.

35

사용자 이름이 유효하지 않거나 암호가 올바르지 않은 경우 예외가 아닙니다. 이것들은 정상적인 작동 흐름에서 기대해야 할 것입니다. 예외는 정상적인 프로그램 작업의 일부가 아니며 드물다.

편집 : 메소드를 호출하는 것만으로 예외를 throw하는지 알 수 없기 때문에 예외 사용을 좋아하지 않습니다. 따라서 예외를 적절한 방식으로 처리 할 수없는 경우에만 예외를 사용해야합니다 ( "메모리 부족"또는 "컴퓨터가 작동 중"이라고 생각).


"나는 단지 호출을보고 메소드가 예외를 던지는 지 알 수 없기 때문에 예외를 사용하는 것을 좋아하지 않는다."
Newtopian

1
확인 된 예외에는 고유 한 문제가 있습니다. 나는 여전히 일반적인 워크 플로우의 일부가 아닌 "예외 상황"의 예외를 사용하고 싶습니다.
EricSchaefer

2
편집 한 내용에 대한 답변. 나는 항상 요약 섹션의 끝에 내 XML 문서에 함수가 던지는 예외를 넣어서 정보를 지능적으로 볼 수 있도록했습니다.
Matthew Vines

1
사용자가 웹 페이지 코드를 조작하여 허용되지 않는 불법적 인 행동을 시도 할 때 예외가 발생해야합니다 (예 : StackOverflow에서 다른 사람의 게시물 삭제).
Rajat Gupta

1
오류 처리 (메모리, 컴퓨터 사용 중) 대 코드 예외 처리 (레코드 누락, 잘못된 입력 유형 등)에 대해 이야기하는 것처럼 들립니다. 둘 사이에는 뚜렷한 차이가 있다고 생각합니다.
척 버지스

28

일반적으로 예측할 수없는 경우 예외를 사용하는 것이 좋습니다. 데이터베이스 연결, 디스크에 파일이없는 등의 예가 있습니다. 예측할 수있는 시나리오 (예 : 잘못된 비밀번호로 로그인하려는 사용자는 부울을 리턴하고 상황을 정상적으로 처리하는 방법을 알고있는 기능을 사용해야 함)를 사용해야합니다. 누군가가 암호를 잘못 입력하여 예외를 throw하여 갑자기 실행을 끝내고 싶지 않습니다.


6
예외에서 프로그램 실행을 중지 할 필요가 없습니다 ... 예외를 던지면 호출자는 실행을 포착하고 가능하면 로그 및 오류를 처리하고 계속 처리해야합니다. 콜 스택에 예외를 계속 던지는 것이 '나쁜 형태'입니다. 발생하는 곳을 잡아서 처리하십시오.
Krakkos

2
그러나 직접 처리 할 수 ​​있다면 왜 던질 수도 있습니다. 암호가 틀리거나 잘못된 것이 있으면 그냥 false를 반환하고 오류를 내 보냅니다
My1

" 디스크에 파일이 없습니다 ".NET 프레임 워크와 같은 대부분의 언어 프레임 워크는 파일 존재 여부를 확인하는 API도 제공합니다. 파일에 직접 액세스하기 전에 파일을 사용하지 마십시오!
user1451111

23

다른 사람들은 사용자가 잘못 입력하면 정상적인 흐름으로 잘못된 로그인이 예상되므로 예외를 사용해서는 안된다고 제안합니다. 나는 동의하지 않으며 추론을 얻지 못한다. 파일을 여는 것과 비교해보십시오. 파일이 존재하지 않거나 어떤 이유로 사용할 수없는 경우 프레임 워크에서 예외가 발생합니다. 위의 논리를 사용하는 것은 Microsoft의 실수였습니다. 오류 코드를 반환해야합니다. 파싱, 웹 요청 등에도 동일합니다.

정상적인 흐름의 잘못된 로그인 부분을 고려하지 않습니다. 예외적입니다. 일반적으로 사용자는 올바른 비밀번호를 입력하고 파일이 존재합니다. 예외적 인 경우는 예외적이며 예외를 사용하는 것이 좋습니다. 스택의 n 레벨을 통해 반환 값을 전파하여 코드를 복잡하게 만드는 것은 에너지 낭비이며 코드가 지저분해질 수 있습니다. 가능한 가장 간단한 일을하십시오. 오류 코드를 사용하여 조기에 최적화하지 마십시오. 정의에 따른 예외적 인 일은 거의 발생하지 않으며 예외를 발생시키지 않으면 비용이 발생하지 않습니다.


당신은 물론 프레임 워크에 따라 open을 호출하기 전에 파일이 존재하는지 확인할 수 있습니다. 따라서 기능이 존재하므로 검사와 파일 사이에서 파일이 사라지면 파일을 여는 시도는 예외입니다.
blowdart

7
파일이 존재한다고해서 사용자가 파일에 쓸 수있는 것은 아닙니다. 가능한 모든 문제를 확인하는 것은 정말 지루하고 오류가 발생하기 쉽습니다. + 코드를 복제하고 있습니다 (DRY).
Bjorn Reppen

비밀번호가 유효하지 않은 예외는 리턴 코드 솔루션과 비교할 때 사용자가 비밀번호를 입력 할 때 속도가 느려질 수 없다는 것입니다.
paperhorse

7
"스택 위로 n 레벨을 통해 반환 값을 전파하여 코드를 복잡하게 만드는 것은 에너지 낭비이며 복잡한 코드가 발생합니다." 저에게는 예외를 사용해야하는 매우 강력한 이유가 있습니다. 좋은 코드는 일반적으로 작은 기능으로 구성됩니다. 하나의 작은 함수에서 다른 함수로 해당 오류 코드를 반복해서 전달하고 싶지 않습니다.
beluchin 2016 년

나는 아마도 login-type 메소드 의 예측 가능한 결과 는 암호가 틀릴 수 있고 실제로 이것을 결정하는 데 사용될 수 있으며이 경우 예외를 원하지 않을 것이라는 가정에서 혼란을 초래한다고 생각합니다 . 반면에 file open입력 시나리오 나 외부 요인으로 인해 시스템이 결과를 전달할 수없는 경우에는 특정 결과가 원하는 유형 시나리오에서 예외를 완벽하게 논리적으로 사용합니다.
theMayer

17

예를 들어, 잘못된 암호를 제공하는 사용자가있는 경우 예외는 다소 비용이 많이 드는 영향입니다. 일반적으로 실패 플래그 나 다른 잘못된 표시기를 다시 전달하는 것이 좋습니다.

이는 예외 처리 방식, 잘못된 입력 오류 및 고유 한 중요 중지 항목은 예외 여야하지만 로그인 정보는 실패하지 않기 때문입니다.


14

현재 상태에서 벗어날 수있는 방법이 없을 때만 예외를 throw해야한다고 생각합니다. 예를 들어 메모리를 할당하고 할당 할 메모리가없는 경우. 언급 한 경우 해당 상태에서 명확하게 복구하고 그에 따라 오류 코드를 호출자에게 다시 반환 할 수 있습니다.


이 질문에 대한 답변을 포함하여 "예외"상황에서만 예외를 처리해야한다는 충고가 많이 있습니다. 그것은 겉으로는 합리적이지만, 하나의 질문 ( "예외를 던져야 할 때")을 다른 주관적인 질문 ( "특별한 것")으로 대체하기 때문에 잘못된 조언이다. 대신 Herb Sutter의 조언을 따르십시오 (C ++의 경우 Dr Dobbs 기사 예외 사용 방법 및 사용 방법 및 Andrei Alexandrescu와 함께하는 C ++ 코딩 표준 )에서 예외를 처리하십시오.

  • 전제 조건이 충족되지 않거나 (일반적으로 다음 중 하나가 불가능 함)
  • 대안은 사후 조건을 충족시키지 못하거나
  • 대안은 불변성을 유지하지 못할 것입니다.

왜 이것이 더 낫습니까? 전제 조건, 사후 조건 및 변형에 대한 몇 가지 질문으로 질문을 대체하지 않습니까? 이것은 몇 가지 연결된 이유로 더 좋습니다.

  • 사전 조건 및 사후 조건 불변량은 설계 로 결정하는 반면, 센스 프로그램 (내부 API)의 특징 throw을 구현 세부 사항이다. 디자인과 구현을 개별적으로 고려해야한다는 점을 명심해야하며, 방법을 구현하는 동안 디자인 제약 조건을 만족하는 무언가를 만드는 것이 우리의 임무입니다.
  • 전제 조건, 사후 조건 및 불변량에 대해 생각하도록 강요합니다. 이는 전제 조건 의 메소드 호출자가 작성해야하는 유일한 가정이며 정확하게 표현되어 프로그램 구성 요소 사이의 느슨한 결합을 가능하게합니다.
  • 그런 느슨한 결합은 필요한 경우 구현을 리팩토링 할 수 있습니다.
  • 사후 조건과 불변은 테스트 가능합니다. 사후 조건은 우리의 단위 테스트 코드가 확인 (어설 션) 할 수있는 조건이기 때문에 쉽게 단위 테스트 할 수있는 코드가됩니다.
  • 사후 조건으로 생각하면 자연스럽게 사후 조건으로 성공한 디자인이 생성 되는데, 이는 예외를 사용하기위한 자연스러운 스타일입니다. 프로그램의 정상 ( "행복한") 실행 경로는 선형으로 배치되며 모든 오류 처리 코드가 catch절로 이동됩니다 .

10

예외 사용시기에 대한 강력하고 빠른 규칙은 없다고 말하고 싶습니다. 그러나 그것들을 사용하거나 사용하지 않는 좋은 이유가 있습니다.

예외를 사용해야하는 이유 :

  • 일반적인 경우의 코드 흐름이 더 명확합니다.
  • 복잡한 오류 정보를 객체로 반환 할 수 있습니다 (이것은 참조로 전달 된 오류 "out"매개 변수를 사용하여 달성 할 수도 있음)
  • 언어는 일반적으로 예외 발생시 정리 정리를 관리 할 수있는 기능을 제공합니다 (C #에서는 RAII, C ++에서는 RAII 사용).
  • 예외가 슬로우되지 않는 경우, 실행 할 수 있습니다 때로는 빠르게 반환 코드를 검사보다
  • Java에서는 확인 된 예외를 선언하거나 포착해야합니다 (이에 대한 이유 일 수 있음).

예외를 사용하지 않는 이유 :

  • 오류 처리가 간단한 경우 때때로 과잉입니다
  • 예외가 문서화되거나 선언되지 않은 경우 호출 코드에 의해 예외가 포착되지 않을 수 있는데, 이는 호출 코드가 리턴 코드를 방금 무시한 것보다 더 나쁠 수 있습니다 (애플리케이션 종료 대 자동 실패-시나리오에 따라 더 나빠질 수 있음)
  • C ++에서 예외를 사용하는 코드는 예외 안전해야합니다 (던지지 않거나 잡지 않고 간접적으로 던지기 함수를 호출하더라도)
  • C ++에서는 함수가 언제 발생할 수 있는지 알기가 어렵 기 때문에 예외 안전에 대해 편집증을 사용해야합니다
  • 예외를 던지고 잡는 것은 일반적으로 반환 플래그를 확인하는 것보다 훨씬 비쌉니다.

일반적으로 C ++ 또는 C #보다 Java에서 예외를 사용하는 경향이 더 큽니다. 예외 선언을 변경하면 예외가 변경 될 수 있기 때문에 예외가 기본적으로 함수의 공식 인터페이스에 속한다고 생각하기 때문입니다. 호출 코드를 끊습니다. Java IMO에서 그것들을 사용하는 가장 큰 장점은 호출자가 예외를 처리해야한다는 것을 알고 있으며, 이는 올바른 행동의 가능성을 향상시킵니다.

이로 인해 모든 언어에서 항상 공통 클래스에서 코드 또는 API 계층의 모든 예외를 파생하므로 호출 코드는 항상 모든 예외를 잡을 수 있습니다. 또한 API 또는 라이브러리를 작성할 때 구현에 특화된 예외 클래스를 던지는 것이 좋지 않다고 생각합니다 (즉, 호출자가받는 예외를 인터페이스의 컨텍스트에서 이해할 수 있도록 하위 계층에서 예외를 래핑하십시오).

Java는 일반적인 예외와 런타임 예외를 구별 할 수 없다는 점에 유의하십시오. 후자는 선언 할 필요가 없습니다. 오류가 프로그램의 버그의 결과라는 것을 알고있을 때만 런타임 예외 클래스를 사용합니다.


5

예외 클래스는 "일반"클래스와 같습니다. 필드와 조작이 다른 다른 유형의 객체가 "있는"경우 새 클래스를 작성합니다.

경험상 예외 수와 예외 세분화 간의 균형을 유지해야합니다. 메소드가 4-5 개 이상의 서로 다른 예외를 발생시키는 경우, 일부 예외를보다 "일반적인"예외 (예 : "AuthenticationFailedException")로 병합하고 예외 메시지를 사용하여 무엇이 잘못되었는지 자세히 설명 할 수 있습니다. 코드가 각각을 다르게 처리하지 않는 한 많은 예외 클래스를 만들 필요가 없습니다. 그리고 그렇다면 오류가 발생한 열거 형을 반환해야 할 수도 있습니다. 이런 식으로 조금 더 깨끗합니다.


5

루프 내에서 코드를 반복 실행하여 예외를 반복적으로 발생시키는 경우 예외를 던지는 것은 큰 N의 경우 속도가 느리기 때문에 좋은 것은 아닙니다. 그러나 성능이 좋지 않은 경우 사용자 지정 예외를 던지는 데 아무런 문제가 없습니다. 이슈. BaseException 또는 이와 유사한 것으로 모두 상속되는 기본 예외가 있는지 확인하십시오. BaseException은 System.Exception을 상속하지만 모든 예외는 BaseException을 상속합니다. 예외 유형의 트리를 사용하여 유사한 유형을 그룹화 할 수도 있지만 과도하거나 과도하지 않을 수 있습니다.

따라서 짧은 대답은 상당한 성능 저하를 초래하지 않으면 (많은 예외를 던지지 않는 한 안됩니다) 계속하십시오.


루프 내부 예외에 대한 귀하의 의견을 정말로 좋아했으며 직접 시도해 보았습니다. 루프 int.MaxValue시간을 실행 하고 그 안에 'divide by zero'예외를 생성 하는 샘플 프로그램을 작성 했습니다. 나누기 작업 전에 피제수가 0이 아닌지 확인한 IF / ELSE 버전 6082ms 및 15407722 틱으로 완료되었지만 예외를 생성하고 예외를 잡는 TRY / CATCH 버전 은 28174385에서 완료되었습니다. ms 및 71371326155 틱 : if / else 버전보다 4632 배나 더 큽니다.
user1451111 2018 년

3

나는 거기에 japollock 방법에 동의합니다-당신이 수술의 결과에 대해 확실하지 않을 때 수락을 던집니다. API 호출, 파일 시스템 액세스, 데이터베이스 호출 등. 프로그래밍 언어의 "경계"를 지나갈 때마다.

추가하고 싶습니다. 표준 예외를 자유롭게 던지십시오. "다른"(무시, 이메일, 로그, 트위터 고래 그림 표시 등) 작업을 수행하지 않는 한 사용자 지정 예외를 신경 쓰지 마십시오.


3

예외를 던지기위한 경험 법칙은 매우 간단합니다. 코드가 UNRECOVERABLE INVALID 상태가되면 그렇게합니다. 데이터가 손상되었거나 특정 시점까지 발생한 처리를 되돌릴 수없는 경우 종료해야합니다. 실제로 다른 무엇을 할 수 있습니까? 처리 로직은 결국 다른 곳에서 실패합니다. 어떻게 든 복구 할 수 있다면 예외를 throw하지 마십시오.

특별한 경우에 돈 인출을 수락하는 것과 같은 바보 같은 일을 강요 당하고 사용자 / 암호 만 확인하는 경우 예외가 발생하여 잘못된 일이 발생했음을 알리고 추가 손상을 방지함으로써 프로세스를 종료해야합니다.


2

일반적으로 응용 프로그램에서 발생할 수있는 "예외"에 대해서는 예외를 throw하려고합니다.

귀하의 예에서 두 예외는 모두 암호 / 사용자 이름 유효성 검사를 통해 호출하는 것처럼 보입니다. 이 경우 누군가가 사용자 이름 / 비밀번호를 잘못 입력하는 것이 예외적이지 않다고 주장 할 수 있습니다.

그것들은 UML의 주요 흐름에 대한 "예외"이지만 처리 과정에서 더 많은 "분기"입니다.

passwd 파일 또는 데이터베이스에 액세스하려고 시도했지만 액세스 할 수없는 경우 예외가되며 예외가 발생합니다.


" passwd 파일 또는 데이터베이스에 액세스하려고 시도했지만 액세스 할 수없는 경우 예외가 될 수 있습니다. .NET 프레임 워크와 같은 대부분의 언어 프레임 워크는 파일의 존재 여부를 확인하는 API도 제공합니다. 파일에 직접 액세스하기 전에 파일을 사용하지 마십시오!
user1451111

2

첫째, API 사용자가 구체적이고 세분화 된 실패에 관심이 없다면 특정 예외를 갖는 것은 가치가 없습니다.

사용자에게 유용한 정보를 알 수없는 경우가 많으므로 더 좋은 방법은 특정 예외를 갖지만 공통 클래스 (예 : std :: exception 또는 C ++의 파생물)에서 상속되도록하는 것입니다. 이를 통해 고객은 원하는 경우 특정 예외를, 또는 관심없는 경우보다 일반적인 예외를 포착 할 수 있습니다.


2

예외는 비정상적인 동작, 오류, 실패 등의 이벤트에 적용됩니다. 기능적 동작, 사용자 오류 등은 대신 프로그램 로직으로 처리해야합니다. 잘못된 계정이나 암호는 로그인 루틴의 논리 흐름에서 예상되는 부분이므로 예외없이 이러한 상황을 처리 할 수 ​​있어야합니다.


2

예외 사용에 철학적 문제가 있습니다. 기본적으로 특정 시나리오가 발생할 것으로 예상하지만 명시 적으로 처리하지 않고 문제를 "다른 곳"으로 처리하도록합니다. 그리고 그 "다른 곳"이 어디에 있는지는 누구나 추측 할 수 있습니다.


2

나는 일반적으로 모든 근본주의가 지옥으로 이어진다 고 말하고 싶습니다.

확실히 예외 구동 흐름으로 끝나고 싶지는 않지만 예외를 피하는 것도 나쁜 생각입니다. 두 가지 접근법 사이의 균형을 찾아야합니다. 내가하지 않는 것은 모든 예외 상황에 대해 예외 유형을 만드는 것입니다. 생산적이지 않습니다.

내가 일반적으로 선호하는 것은 시스템 전체에서 사용되는 두 가지 기본 유형의 예외 인 LogicalExceptionTechnicalException 을 만드는 것 입니다. 필요한 경우 하위 유형으로 구분할 수 있지만 일반적으로 필요하지는 않습니다.

기술 예외는 데이터베이스 서버가 다운되거나 웹 서비스에 대한 연결이 IOException을 발생시키는 등 예상치 못한 예외를 나타냅니다.

반면에 논리적 예외는 덜 심각한 오류 상황을 상위 계층 (일반적으로 일부 유효성 검사 결과)으로 전파하는 데 사용됩니다.

논리적 예외조차도 프로그램 흐름을 제어하기 위해 정기적으로 사용되는 것이 아니라 흐름이 실제로 끝나야하는 상황을 강조하기위한 것입니다. Java에서 사용되는 경우 두 예외 유형 모두 RuntimeException 서브 클래스이며 오류 처리는 측면 지향적입니다.

따라서 로그인 예제에서 AuthenticationException과 같은 것을 생성하고 UsernameNotExisting 과 같은 열거 형 값으로 구체적인 상황을 구별하는 것이 현명 할 수 있습니다. , PasswordMismatch 등과 . 그러면 큰 예외 계층 구조를 가지지 않고 catch 블록을 유지 보수 가능한 수준으로 유지할 수 있습니다 . 예외를 분류하고 사용자에게 전파 할 대상과 방법을 잘 알고 있기 때문에 일반적인 예외 처리 메커니즘을 쉽게 사용할 수 있습니다.

일반적인 사용법은 사용자 입력이 유효하지 않은 웹 서비스 호출 중에 LogicalException을 발생시키는 것입니다. 예외는 SOAPFault 세부 사항으로 마샬링 된 다음 클라이언트에서 다시 예외로 비 정렬 화되어 예외가 해당 필드에 올바르게 맵핑되므로 특정 웹 페이지 입력 필드 중 하나에 유효성 검증 오류가 표시됩니다.

이것은 반드시 유일한 상황은 아닙니다. 예외를 던지기 위해 웹 서비스를 칠 필요는 없습니다. 예외 상황 (페일 패스트가 필요한 경우 등)에서 자유롭게 할 수 있습니다. 이는 모두 귀하의 재량에 달려 있습니다.


2

나를 위해 필요한 기술 또는 비즈니스 규칙이 실패하면 예외가 발생합니다. 예를 들어, 자동차 엔티티가 4 개의 타이어 배열과 연관되어있는 경우 ... 하나 이상의 타이어가 null 인 경우 ... 예외 "NotEnoughTiresException"을 발생시켜야합니다. 로깅을 통한 의미. 우리가 그냥 흐름 제어를 시도하고 자동차의 instanciation을 방지하려는 경우 외에. 우리는 결코 문제의 원인을 찾지 못할 것입니다. 처음에는 타이어가 null이되지 않아야합니다.


1

예외 발생을 피하는 주된 이유는 예외 발생과 관련된 많은 오버 헤드가 있기 때문입니다.

아래 기사에서 언급 한 한 가지 예외는 예외적 인 조건과 오류에 대한 예외입니다.

잘못된 사용자 이름은 반드시 프로그램 오류 일 필요는 없지만 사용자 오류입니다 ...

다음은 .NET 내의 예외에 대한 적절한 시작점입니다. http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

예외가 발생하면 스택이 풀리고 성능에 약간의 영향을 미칩니다 (현대 관리되는 환경이 개선되었습니다). 중첩 된 상황에서 여전히 예외를 반복적으로 발생시키고 포착하는 것은 나쁜 생각입니다.

아마도 그보다 더 중요한 예외는 예외적 인 조건에 대한 것입니다. 코드의 가독성을 떨어 뜨리므로 일반적인 제어 흐름에는 사용하면 안됩니다.


1

내가 잡는 세 가지 유형의 조건이 있습니다.

  1. 입력이 잘못되었거나 누락 된 것은 예외가 아니어야합니다. 클라이언트 측 j와 서버 측 정규 표현식을 모두 사용하여 속성을 감지하고 설정 한 후 메시지가있는 동일한 페이지로 다시 전달하십시오.

  2. AppException. 이것은 일반적으로 코드에서 감지하여 처리하는 예외입니다. 다시 말해, 이들은 당신이 기대하는 것입니다 (파일이 존재하지 않습니다). 로그를 작성하고 메시지를 설정 한 후 일반 오류 페이지로 다시 전달하십시오. 이 페이지에는 일반적으로 무슨 일이 있었는지에 대한 약간의 정보가 있습니다.

  3. 예기치 않은 예외. 이것들은 당신이 모르는 것입니다. 세부 사항과 함께이를 기록하고 일반 오류 페이지로 전달하십시오.

도움이 되었기를 바랍니다


1

보안은 다음과 같은 예와 관련이 있습니다. 공격자에게 사용자 이름이 존재하지만 암호가 잘못되었음을 알리지 않아야합니다. 공유 할 필요가없는 추가 정보입니다. "사용자 이름 또는 비밀번호가 잘못되었습니다"라고 말합니다.


1

간단한 대답은 조작이 불가능할 때마다 (어플리케이션 또는 비즈니스 로직을 위반하기 때문에)입니다. 메소드가 호출되어 메소드 작성을 수행 할 수없는 경우 예외를 처리하십시오. 좋은 예는 제공된 매개 변수를 사용하여 인스턴스를 만들 수없는 경우 생성자가 항상 ArgumentExceptions를 발생시키는 것입니다. 다른 예는 InvalidOperationException이며, 이는 다른 멤버 또는 클래스 멤버의 상태로 인해 조작을 수행 할 수 없을 때 발생합니다.

귀하의 경우, Login (username, password)과 같은 메소드가 호출 된 경우, 사용자 이름이 유효하지 않은 경우, 실제로 UserNameNotValidException을 발생시키는 것이 정확합니다. 제공된 매개 변수를 사용하여 사용자를 로그인 할 수 없습니다 (즉, 인증을 위반하기 때문에 불가능 함). 예외를 발생시킵니다. 비록 당신의 두 예외가 ArgumentException에서 상속받을 수도 있습니다.

로그인 실패가 매우 일반적 일 수 있기 때문에 예외를 발생시키지 않으려면 한 가지 전략은 다른 실패를 나타내는 유형을 리턴하는 메소드를 작성하는 것입니다. 예를 들면 다음과 같습니다.

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

대부분의 개발자는 예외로 인해 발생하는 오버 헤드로 인해 예외를 피해야합니다. 리소스를 사용하는 것이 좋지만 일반적으로 응용 프로그램 디자인을 희생하지는 않습니다. 아마 두 예외를 던지지 말라고 들었을 것입니다. 예외 사용 여부는 일반적으로 예외 발생 빈도로 요약됩니다. 상당히 흔하거나 기대할 수있는 결과 인 경우 대부분의 개발자가 예외를 피하고 대신 리소스 소비로 인해 실패를 나타내는 다른 방법을 만드는 경우입니다.

다음은 Try () 패턴을 사용하여 방금 설명한 시나리오에서 예외 사용을 피하는 예입니다.

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

내 생각에 근본적인 질문은 조건이 발생하면 호출자가 정상적인 프로그램 흐름을 계속하기를 원할지 여부입니다. 모르는 경우 별도의 doSomething 및 trySomething 메소드를 사용하십시오. 전자가 오류를 리턴하고 후자가 그렇지 않은 경우 또는 예외가 실패 할 경우 예외를 처리해야하는지 여부를 표시하는 매개 변수를 승인하는 루틴이 있습니다). 원격 시스템에 명령을 보내고 응답을보고하는 클래스를 고려하십시오. 특정 명령 (예 : 다시 시작)으로 인해 원격 시스템이 응답을 보내지 만 일정 시간 동안 응답하지 않습니다. 따라서 "ping"명령을 보내고 원격 시스템이 예외를 발생시키지 않으면 서 적당한 시간 내에 응답하는지 여부를 알아내는 것이 유용합니다. t (호출자는 아마도 처음 몇 번의 "ping"시도는 실패 할 것으로 예상하지만 결국에는 작동 할 것입니다). 반면에 다음과 같은 일련의 명령이있는 경우 :

  exchange_command ( "열려있는 임시 파일");
  exchange_command ( "임시 파일 데이터 쓰기 {whatever}");
  exchange_command ( "임시 파일 데이터 쓰기 {whatever}");
  exchange_command ( "임시 파일 데이터 쓰기 {whatever}");
  exchange_command ( "임시 파일 데이터 쓰기 {whatever}");
  exchange_command ( "닫기 임시 파일");
  exchange_command ( "실시간 파일에 임시 파일 복사");

전체 시퀀스를 중단하는 작업의 실패를 원할 것입니다. 각 작업의 성공 여부를 확인할 수 있지만 명령이 실패하면 exchange_command () 루틴에서 예외를 발생시키는 것이 더 도움이됩니다.

실제로, 위 시나리오에서 여러 가지 실패 처리 모드를 선택하는 매개 변수를 갖는 것이 도움이 될 수 있습니다. 예외를 던지지 말고, 통신 오류에 대해서만 예외를 던지거나, 명령이 "성공을 리턴하지 않는 경우에는 예외를 던지십시오. 표시.


1

"PasswordNotCorrectException"은 예외 사용에 대한 좋은 예가 아닙니다. 비밀번호를 잘못 입력 한 사용자는 예상되므로 IMHO도 예외는 아닙니다. 아마도 오류 메시지를 표시하여 복구 할 수도 있으므로 유효성 검사 일뿐입니다.

처리되지 않은 예외는 결국 실행을 중지시킵니다. false, null 또는 오류 코드를 반환하는 경우 프로그램의 상태를 모두 직접 처리해야합니다. 어딘가에서 상태를 확인하는 것을 잊어 버린 경우 프로그램이 계속 잘못된 데이터로 실행되어 알아내는 데 어려움이있을 수 무엇 일어난 .

물론 빈 catch 문에서도 동일한 문제가 발생할 수 있지만 적어도 그 점을 발견하는 것이 더 쉽고 논리를 이해하지 않아도됩니다.

따라서 경험적으로 볼 때 :

원하지 않는 곳이나 단순히 오류를 복구 할 수없는 곳에서 사용하십시오.


0

해당 조건에 대해 약간의 일반적인 예외를 사용할 수 있습니다. 예를 들어 ArgumentException은 매개 변수에 문제가있을 때 사용됩니다 (ArgumentNullException 제외). 일반적으로 LessThanZeroException, NotPrimeNumberException 등과 같은 예외는 필요하지 않습니다. 메소드의 사용자를 생각하십시오. 특히 처리하려는 조건의 수는 메소드에서 발생시켜야하는 예외 유형의 수와 같습니다. 이런 식으로, 당신은 당신이 가질 세부적인 예외를 결정할 수 있습니다.

그건 그렇고, 항상 라이브러리 사용자가 예외를 피할 수있는 몇 가지 방법을 제공하십시오. TryParse는 좋은 예입니다. int.Parse를 사용하지 않고 예외를 잡을 필요가 없습니다. 귀하의 경우 사용자 이름이 유효하거나 암호가 올바른지 확인하는 몇 가지 방법을 제공하여 사용자 (또는 귀하)가 많은 예외 처리를 수행하지 않아도 될 수 있습니다. 이것은 더 읽기 쉬운 코드와 더 나은 성능을 기대합니다.


0

궁극적으로 예외 처리 또는 상태 코드 반환과 같은 자체 홈 메커니즘을 통해 이와 같은 응용 프로그램 수준 오류를 처리하는 것이 더 도움이되는지 결정해야합니다. 나는 어느 것이 더 좋은지에 대한 단단하고 빠른 규칙이 없다고 생각하지만, 다음을 고려할 것입니다.

  • 누가 당신의 코드를 부르고 있습니까? 이것은 일종의 공개 API입니까, 아니면 내부 라이브러리입니까?
  • 어떤 언어를 사용하고 있습니까? 예를 들어 Java 인 경우 (확인 된) 예외를 throw하면 무시할 수있는 반환 상태가 아니라 호출자가이 오류 조건을 처리하는 데 명시적인 부담이 가해집니다. 좋거나 나쁠 수 있습니다.
  • 동일한 응용 프로그램의 다른 오류 조건은 어떻게 처리됩니까? 호출자는 시스템의 다른 것과 달리 특유의 방식으로 오류를 처리하는 모듈을 다루기를 원하지 않습니다.
  • 문제가되는 일상 생활에서 얼마나 많은 일이 잘못 될 수 있으며, 어떻게 다르게 처리 될 것입니까? 다른 오류를 처리하는 일련의 catch 블록과 오류 코드의 스위치 간의 차이점을 고려하십시오.
  • 반환해야 할 오류에 대한 구조화 된 정보가 있습니까? 예외를 던지면 상태를 반환하는 것보다이 정보를 더 잘 배치 할 수 있습니다.

0

예외에는 두 가지 주요 클래스가 있습니다.

1) 시스템 예외 (예 : 데이터베이스 연결 유실) 또는 2) 사용자 예외. (예 : 사용자 입력 확인, '비밀번호가 잘못되었습니다')

내 자신의 User Exception Class를 만드는 것이 도움이되었고 사용자 오류를 던지기를 원할 때 다르게 처리하고 싶을 때 (즉, 사용자에게 리소스 오류가 표시됨) 주 오류 처리기에서해야 할 일은 객체 유형을 확인하는 것입니다. :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

예외가 적절한 지 결정할 때 고려해야 할 몇 가지 유용한 사항 :

  1. 예외 후보가 발생한 후 실행하려는 코드 레벨, 즉 호출 스택 계층을 해제해야하는 계층 수 일반적으로 예외가 발생하는 곳과 최대한 가깝게 예외를 처리하려고합니다. 사용자 이름 / 암호 유효성 검사의 경우 일반적으로 예외가 발생하지 않고 동일한 코드 블록에서 오류를 처리합니다. 따라서 예외는 적절하지 않을 수 있습니다. (OTOH, 세 번의 로그인 시도 실패 후 제어 플로우가 다른 곳으로 이동할 수 있으며 여기서 예외가 적절할 수 있습니다.)

  2. 이 이벤트가 오류 로그에 표시됩니까? 모든 예외가 오류 로그에 기록되는 것은 아니지만 오류 로그의이 항목이 유용한 지 여부를 묻는 것이 유용합니다. 즉, 이에 대해 무언가를 시도하거나 무시하는 가비지가 될 것입니다.

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