오류를 발생시켜야 하는지를 나타내는 플래그가있는 것


64

나는 최근에 훨씬 더 오래된 개발자들 (약 50 세 이상)과 함께 일하기 시작했습니다. 그들은 시스템이 다운 될 수없는 항공을 다루는 중요한 애플리케이션에서 작업했습니다. 결과적으로 이전 프로그래머는 이런 식으로 코딩하는 경향이 있습니다.

그는 예외가 발생해야하는지 여부를 나타 내기 위해 객체에 부울을 넣는 경향이 있습니다.

public class AreaCalculator
{
    AreaCalculator(bool shouldThrowExceptions) { ... }
    CalculateArea(int x, int y)
    {
        if(x < 0 || y < 0)
        {
            if(shouldThrowExceptions) 
                throwException;
            else
                return 0;
        }
    }
}

(이 프로젝트에서는 당시에 존재할 수없는 네트워크 장치를 사용하려고하므로 메소드가 실패 할 수 있습니다. 영역 예제는 예외 플래그의 예일뿐입니다)

나에게 이것은 코드 냄새처럼 보인다. 매번 예외 플래그를 테스트해야하기 때문에 단위 테스트 작성이 약간 더 복잡해집니다. 또한 문제가 발생하면 바로 알고 싶지 않습니까? 계속하는 방법을 결정하는 것은 호출자의 책임이 아니어야합니까?

그의 논리 / 추론은 우리 프로그램이 한 가지 일을해야하며 사용자에게 데이터를 보여야한다는 것입니다. 우리를 방해하지 않는 다른 예외는 무시해야합니다. 나는 그들이 무시해서는 안된다는 것에 동의하지만, 적절한 사람이 거품을 일으켜 다루어야하며, 깃발을 다루지 않아도됩니다.

이것이 예외를 처리하는 좋은 방법입니까?

편집 : 디자인 결정에 대한 더 많은 컨텍스트를 제공하기 위해이 구성 요소가 실패하더라도 프로그램이 여전히 작동하고 주요 작업을 수행 할 수 있기 때문입니다. 따라서 우리는 예외를 던지고 싶지 않으며 (처리하지 않습니까?) 사용자가 잘 작동 할 때 프로그램을 중단시킵니다.

편집 2 : 더 많은 컨텍스트를 제공하기 위해 네트워크 카드를 재설정하기 위해 메소드가 호출됩니다. 네트워크 카드의 연결을 끊었다가 다시 연결하면 다른 IP 주소가 할당되므로 이전 IP로 하드웨어를 재설정하려고 시도하면 Reset에서 예외가 발생합니다.


22
c #에는이 Try-Parse 패턴에 대한 규칙이 있습니다. 추가 정보 : docs.microsoft.com/en-us/dotnet/standard/design-guidelines/… 플래그가이 패턴과 일치하지 않습니다.
피터

18
이것은 기본적으로 제어 매개 변수이며 내부 메소드 실행 방법을 변경합니다. 시나리오에 관계없이 나쁘다. martinfowler.com/bliki/FlagArgument.html , softwareengineering.stackexchange.com/questions/147977/... , medium.com/@amlcurran/...
BIC

1
Peter의 Try-Parse 의견 외에도 Vexing 예외에 대한 유용한 기사가 있습니다. blogs.msdn.microsoft.com/ericlippert/2008/09/10/…
Linaith

2
"따라서 우리는 예외를 처리하고 싶지 않으며 (처리하지 않겠습니까?) 사용자가 잘 작동 할 때 프로그램을 중단 시키려고합니다"-예외를 바로 잡을 수 있다는 것을 알고 있습니까?
user253751

1
나는 이것이 다른 곳보다 먼저 다루어 졌다고 확신하지만 간단한 Area 예제를 통해 그 음수가 어디에서 오는지 궁금하고 다른 곳에서 오류 조건을 처리 할 수 ​​있는지 궁금해 할 것입니다 (예 : 예를 들어 길이와 너비가 포함 된 파일을 읽은 모든 것). 그러나 "현재 존재하지 않는 네트워크 장치를 사용하려고합니다." 요점은 완전히 다른 답변을 얻을 수 있습니다. 이것은 타사 API입니까 아니면 TCP / UDP와 같은 산업 표준입니까?
jrh

답변:


74

이 접근 방식의 문제점은 예외가 발생하지 않으며 (따라서 포착되지 않은 예외로 인해 애플리케이션이 충돌하지 않음) 리턴 된 결과가 반드시 정확하지는 않으며 사용자는 데이터에 문제점이 있음을 알 수 없다는 것입니다. 그 문제는 무엇이며 어떻게 해결 해야하는지).

결과가 정확하고 의미가 있으려면 호출 메소드는 결과를 특수 번호 (예 : 메소드를 실행하는 동안 발생한 문제점을 나타내는 데 사용되는 특정 리턴 값)를 확인해야합니다. 영역과 같이 양의 유한 수량에 대해 반환되는 음수 (또는 0) 숫자는 이전 코드에서 가장 좋은 예입니다. 그러나 호출 방법이 이러한 특수 번호를 알지 못하거나 잊어 버린 경우 실수를 인식하지 않고도 처리를 계속할 수 있습니다. 그런 다음 데이터가 사용자에게 0의 영역을 표시하여 표시되는데, 사용자는이 영역이 틀렸다는 것을 알고 있지만 무엇이 잘못되었는지, 어디서, 왜 발생했는지는 알 수 없습니다. 그런 다음 다른 값 중 하나라도 잘못되었는지 궁금합니다.

예외가 발생하면 처리가 중지되고 (이상적으로) 오류가 기록되고 사용자에게 어떤 방식으로 통지 될 수 있습니다. 그런 다음 사용자는 무엇이 잘못되었는지 수정하고 다시 시도 할 수 있습니다. 적절한 예외 처리 (및 테스트!)는 중요한 응용 프로그램이 충돌하지 않거나 유효하지 않은 상태에서 종료되지 않도록합니다.


1
@Quirk Chen이 단일 책임 원칙을 3 ~ 4 줄로 위반 한 점이 인상적입니다. 그게 진짜 문제입니다. 또한 그가 이야기하는 문제 (프로그래머가 각 줄의 오류 결과에 대해 생각하지 못하는 경우)는 항상 확인되지 않은 예외 의 가능성이며 때로는 확인 된 예외의 가능성입니다. 확인 된 예외에 대한 모든 주장을 보았으며 그중 하나가 유효하지는 않습니다.
TKK

@ TKK 개인적으로 .NET에서 확인 된 예외를 좋아했을 때 어떤 경우가 발생했습니다. 예외가 발생했을 때 API 문서가 정확한지 확인할 수있는 고급 정적 분석 도구가 있다면 좋을 것입니다. 특히 네이티브 리소스에 액세스 할 때는 거의 불가능할 것입니다.
jrh

1
@jrh 그렇습니다. TypeScript kludges가 JS에 유형 안전성을 입력하는 방식과 유사하게 무언가가 .NET에 예외 안전성을 부여한 것이 좋을 것입니다.
TKK

47

이것이 예외를 처리하는 좋은 방법입니까?

아니요, 이것은 꽤 나쁜 습관이라고 생각합니다. 예외를 던지거나 값을 반환하는 것은 API의 근본적인 변화, 메소드의 서명 변경 및 메소드가 인터페이스의 관점과는 상당히 다르게 행동하게 만드는 것입니다.

일반적으로 클래스와 API를 디자인 할 때는 다음 사항을 고려해야합니다.

  1. 같은 프로그램에서 동시에 다른 구성을 가진 클래스의 여러 인스턴스가있을 수 있습니다.

  2. 의존성 주입과 다른 많은 프로그래밍 관행으로 인해 한 소비 클라이언트는 객체를 생성하고 다른 객체를 다른 용도로 사용할 수 있습니다. 따라서 종종 객체 생성자와 객체 사용자가 분리됩니다.

예를 들어 계산 방법을 호출하기 위해 인스턴스를 사용하기 위해 호출자가 수행해야 할 작업을 고려하십시오. 테스트 고려 사항은 클래스 자체뿐만 아니라 호출자의 오류 처리에도 적용됩니다.

우리는 항상 고객을 위해 가능한 한 쉽게 일을해야합니다. 인스턴스 메소드의 API를 변경하는 생성자의 부울 구성은 소비 클라이언트 프로그래머 (아마도 당신 또는 동료)가 성공의 구덩이에 빠지는 것과 반대입니다.

두 API를 모두 제공하려면 오류가 발생하는 클래스와 오류가 발생하면 항상 0을 반환하는 클래스, 또는 단일 클래스로 두 개의 다른 메소드를 제공하는 두 가지 다른 클래스를 제공하는 것이 훨씬 더 좋고 정상적입니다. 이렇게하면 소비 클라이언트가 오류를 확인하고 처리하는 방법을 정확하게 알 수 있습니다.

두 개의 서로 다른 클래스 또는 두 개의 다른 메소드를 사용하면 IDE를 사용하여 메소드 사용자 및 리 팩터 기능 등을 훨씬 더 쉽게 사용할 수 있습니다. 코드 읽기, 쓰기, 유지 관리, 검토 및 테스트도 더 간단합니다.


또 다른 참고로, 개인적으로 실제 호출자가 모두 상수를 전달하는 부울 구성 매개 변수를 사용해서는 안된다고 생각합니다 . 이러한 구성 매개 변수화는 실질적인 이점없이 두 개의 개별 사용 사례를 병합합니다.

코드베이스를 살펴보고 생성자의 부울 구성 매개 변수에 변수 (또는 상수가 아닌 표현식)가 사용되는지 확인하십시오! 나는 그것을 의심한다.


또한 영역 계산에 실패 할 수있는 이유를 추가로 고려해야합니다. 계산을 할 수 없으면 생성자를 던지는 것이 가장 좋습니다. 그러나 객체가 추가로 초기화 될 때까지 계산을 수행 할 수 있는지 여부를 모르는 경우 다른 클래스를 사용하여 해당 상태를 구분할 수 있습니다 (영역을 계산할 수 없음과 영역을 계산할 수 있음).

귀하의 실패 상황이 원격으로 향하고 있으므로 적용되지 않을 수 있음을 읽었습니다. 생각할 음식이 있습니다.


계속하는 방법을 결정하는 것은 호출자의 책임이 아니어야합니까?

그래, 난 동의. 호출자가 0의 영역이 오류 조건에서 정답이라고 결정하는 것은 너무 이른 것 같습니다 (특히 0은 유효한 영역이므로 오류와 실제 0의 차이를 알 수있는 방법은 없지만 앱에는 적용되지 않을 수 있습니다).


메소드 호출 하기 전에 인수를 확인해야하기 때문에 예외를 전혀 확인할 필요가 없습니다 . 0에 대해 결과를 확인한다고해서 유효한 인수 0, 0과 잘못된 부정 인수를 구별하지는 않습니다. API는 정말 끔찍한 IMHO입니다.
BlackJack

C99 및 C ++ iostream에 대한 Annex K MS 푸시는 후크 또는 플래그가 반응을 실패로 근본적으로 변경하는 API의 예입니다.
중복 제거기

37

그들은 시스템이 다운 될 수없는 항공을 다루는 중요한 애플리케이션에서 작업했습니다. 결과적으로 ...

이것은 흥미로운 디자인입니다.이 디자인의 동기는 "시스템이 다운 될 수 있기 때문에" 어떤 상황에서 예외를 던지는 것을 피하는 것 입니다. 그러나 "예외로 인해 시스템이 다운 될 수있는 경우"는

  • 예외는 적어도 적절하게 처리되지 않습니다 .

따라서이 프로그램을 사용하는 프로그램 AreaCalculator이 버그가있는 경우, 동료는 프로그램이 "초기 충돌"을하지 않고 잘못된 값을 반환하는 것을 선호합니다 (아무도 눈치 채지 않거나 아무도 중요한 일을하지 않기를 바랍니다). 그것은 실제로 오류를 숨기고 있으며 , 내 경험상 조만간 후속 버그가 발생하여 근본 원인을 찾기가 어려워집니다.

IMHO는 어떤 상황에서도 충돌하지 않지만 잘못된 데이터 나 계산 결과를 보여주는 프로그램을 작성하는 것이 일반적으로 프로그램 충돌을 일으키는 것보다 낫지 않습니다. 유일한 올바른 접근 방법은 호출자에게 오류를 알리고 처리하고 사용자에게 잘못된 동작에 대한 정보를 제공해야하는지, 처리를 계속하는 것이 안전한지 또는 더 안전한지 결정하도록하는 것입니다. 프로그램을 완전히 중지합니다. 따라서 다음 중 하나를 권장합니다.

  • 함수가 예외를 던질 수 있다는 사실을 간과하기 어렵게 만듭니다. 문서 및 코딩 표준이 여기에 있습니다. 정기적 인 코드 검토는 구성 요소의 올바른 사용법과 적절한 예외 처리를 지원해야합니다.

  • "블랙 박스"컴포넌트를 사용할 때 예외를 예상하고 처리하도록 팀을 교육하고 프로그램의 전체적인 동작을 염두에 두십시오.

  • 어떤 이유로 든 예외 처리를 올바르게 사용하기 위해 호출 코드 (또는 코드를 작성하는 개발자)를 얻을 수 없다고 생각되면 최후의 수단으로 명시 적 오류 출력 변수가있는 API를 설계하고 예외는 전혀 발생하지 않습니다.

    CalculateArea(int x, int y, out ErrorCode err)

    따라서 호출자가 함수 실패를 간과하기가 정말 어려워집니다. 그러나 이것은 C #에서 매우 못생긴 IMHO입니다. 그것은 예외가없는 C의 오래된 방어 프로그래밍 기술이며, 일반적으로 오늘날에는 작동하지 않아도됩니다.


3
"어떤 상황에서도 충돌하지는 않지만 잘못된 데이터 또는 계산 결과를 보여주는 프로그램을 작성하는 것이 일반적으로 프로그램 충돌을 일으키는 것보다 낫지는 않습니다."나는 항공에서 비행기를 선호한다고 생각할 수는 있지만 일반적으로 완전히 동의합니다. 비행기 컴퓨터가 셧다운되는 것과 비교할 때 여전히 잘못된 값을 표시하는 계측기를 사용합니다. 덜 중요한 응용 프로그램의 경우 오류를 가리지 않는 것이 좋습니다.
Trilarion

18
@Trilarion : 비행 컴퓨터 용 프로그램에 적절한 예외 처리가 포함되어 있지 않은 경우 구성 요소에서 예외를 발생시키지 않도록 "고정"하는 것은 매우 잘못된 방법입니다. 프로그램이 충돌하면 대체 할 수있는 여분의 백업 시스템이 있어야합니다. 예를 들어 프로그램이 충돌하지 않고 높이가 잘못 표시되면 비행기가 다음 산으로 돌진하는 동안 조종사는 "모든 것이 정상"이라고 생각할 수 있습니다.
Doc Brown

7
@Trilarion : 비행 컴퓨터의 높이가 잘못 표시되어 이로 인해 비행기가 추락하는 경우 도움이되지 않습니다 (특히 백업 시스템이 있고이를 인수해야한다는 정보를받지 못한 경우). 비행기 컴퓨터의 백업 시스템은 "비행기 컴퓨터 백업 시스템"이라는 새로운 아이디어가 아닙니다. 전 세계의 엔지니어가 항상 실제 시스템에 중요한 시스템에 중복 시스템을 구축했다고 확신합니다. 보험).
Doc Brown

4
이. 프로그램이 중단 될 여유가 없다면, 조용히 틀린 답을 줄 여유가 없습니다. 정답은 모든 경우 에 적절한 예외 처리를 하는 것입니다. 웹 사이트의 경우 이는 예기치 않은 오류를 500으로 변환하는 전역 처리기를 의미 합니다. 한 요소가 실패해도 처리를 계속해야하는 경우 루프 내부에 try/ catch가있는 것과 같이보다 구체적인 상황에 대한 추가 처리기가있을 수도 있습니다 .
jpmc26

2
잘못된 결과를 얻는 것은 항상 최악의 실패입니다. 최적화에 대한 규칙을 상기시켜줍니다. "오답을 더 빨리 얻는 것은 여전히 ​​누구에게도 도움이되지 않기 때문에 최적화하기 전에 올바르게하십시오."
Toby Speight

13

매번 예외 플래그를 테스트해야하기 때문에 단위 테스트 작성이 약간 더 복잡해집니다.

n 개의 매개 변수가있는 함수 는 n-1 매개 변수가있는 함수 보다 테스트하기가 더 어려워집니다 . 그것을 터무니없는 것으로 확장하면 인수는 테스트하기가 쉽기 때문에 함수에 매개 변수가 없어야한다고 주장합니다.

테스트하기 쉬운 코드를 작성하는 것이 좋은 생각이지만 코드를 작성하는 사람들에게 유용한 코드 작성보다 테스트 단순성을 두는 것이 끔찍한 아이디어입니다. 질문의 예에 예외가 발생했는지 여부를 결정하는 스위치가있는 경우 해당 동작을 원하는 발신자 수는 예외를 함수에 추가 할 수 있습니다. 복잡한 거짓말과 너무 복잡한 거짓말 사이의 경계는 판단력입니다. 모든 상황에 적용되는 밝은 선이 있다고 말하려는 사람은 누구나 의심해야합니다.

또한 문제가 발생하면 바로 알고 싶지 않습니까?

그것은 당신의 잘못에 대한 당신의 정의에 달려 있습니다. 이 질문의 예는 잘못된 것을 "제로보다 작은 차원을 부여 받았으며"로 정의 shouldThrowExceptions합니다. shouldThrowExceptions스위치가 다른 동작을 유발하기 때문에 false 일 때 0보다 작은 경우 치수를 지정하는 것은 잘못된 것이 아닙니다 . 그것은 예외적 인 상황이 아닙니다.

여기서 실제 문제는 스위치의 기능이 무엇인지 설명하지 않기 때문에 스위치 이름이 잘못되었다는 것입니다. 와 같은 더 나은 이름이 주어 졌다면 treatInvalidDimensionsAsZero이 질문을 하시겠습니까?

계속하는 방법을 결정하는 것은 호출자의 책임이 아니어야합니까?

발신자 계속하는 방법을 결정합니다. 이 경우 설정 또는 해제 shouldThrowExceptions를 통해 미리 수행하며 기능은 상태에 따라 동작합니다.

이 예제는 단일 계산을 수행하고 반환하기 때문에 병리학 적으로 간단한 예입니다. 숫자 목록의 제곱근의 합계를 계산하는 것과 같이 약간 더 복잡하게 만들면 예외를 던지면 발신자에게 해결할 수없는 문제가 발생할 수 있습니다. 의 목록을 전달 [5, 6, -1, 8, 12]하고 함수에서 예외를 throw -1하면 함수가 이미 중단되어 합계를 버려 버릴 것이므로 함수에게 계속 진행하도록 지시 할 방법이 없습니다. 목록이 거대한 데이터 세트 인 경우 함수를 호출하기 전에 음수없이 사본을 생성하는 것은 실용적이지 않을 수 있으므로 잘못된 숫자를 어떻게 처리 해야하는지 "무시 무시하십시오. "결정을 내리기 위해 호출되는 람다를 전환하거나 제공 할 수 있습니다.

그의 논리 / 추론은 우리 프로그램이 한 가지 일을해야하며 사용자에게 데이터를 보여야한다는 것입니다. 우리를 방해하지 않는 다른 예외는 무시해야합니다. 나는 그들이 무시해서는 안된다는 것에 동의하지만, 적절한 사람이 거품을 일으켜 다루어야하며, 깃발을 다루지 않아도됩니다.

다시 한 번, 모든 솔루션에 맞는 솔루션은 없습니다. 이 예에서 함수는 음의 치수를 처리하는 방법을 설명하는 사양으로 작성된 것으로 추정됩니다. 마지막으로하고 싶은 것은 "일반적으로 예외가 발생하지만 호출자가 귀찮게하지 않겠다"는 메시지로 로그를 채워 로그의 신호 대 잡음비를 낮추는 것입니다.

그리고 훨씬 더 오래된 프로그래머 중 한 사람으로서, 나는 당신이 친절하게 내 잔디밭에서 출발하도록 요청합니다. ;-)


이름과 의도가 매우 중요하다는 데 동의합니다.이 경우 적절한 매개 변수 이름이 실제로 테이블을 돌릴 수 있으므로 +1이지만 1. our program needs to do 1 thing, show data to user. Any other exception that doesn't stop us from doing so should be ignored사고는 사용자가 잘못된 데이터를 기반으로 결정을 내릴 수 있습니다 (실제로 프로그램이 수행해야하기 때문에) 한 가지-사용자가 정보에 입각 한 결정을 내리는 데 도움이 됨) 2. 비슷한 경우 bool ExecuteJob(bool throwOnError = false)는 대개 오류가 발생하기 쉬우 며 코드를 읽는 것만으로 추론하기 어려운 코드로 이어집니다.
유진 포드 칼

@ EugenePodskal "데이터 표시"는 "올바른 데이터 표시"를 의미한다고 가정합니다. 질문자는 완성 된 제품이 작동하지 않는다고 말하지 않고 단지 "잘못된"것으로 쓰여있을 수 있습니다. 두 번째 요점에서 하드 데이터를보아야 할 것입니다. 현재 프로젝트에서 throw / no-throw 스위치가 있고 다른 함수보다 추론하기 어려운 많은 함수를 사용하고 있지만 이는 하나의 데이터 포인트입니다.
Blrfl

좋은 대답은이 논리가 OP보다 훨씬 광범위한 상황에 적용된다고 생각합니다. BTW, cpp의 새로운 기능은 이러한 이유로 정확하게 던지거나 던지지 않는 버전을 가지고 있습니다. 약간의 차이가 있지만 ...
drjpizzle

8

안전이 중요하고 '정상적인'코드는 '좋은 습관'이 어떻게 보이는지에 대해 매우 다른 아이디어로 이어질 수 있습니다. 중복되는 부분이 많으며 일부 항목은 위험하므로 두 가지 모두 피해야합니다. 그러나 여전히 중요한 차이점이 있습니다. 응답 성을 보장하기 위해 요구 사항을 추가하면 이러한 편차가 상당히 커집니다.

이들은 종종 당신이 기대하는 것들과 관련이 있습니다.

  • git의 경우 잘못된 응답은 long / to aborting / hanging 또는 충돌과 관련하여 매우 나쁠 수 있습니다 (실제로 체크인 코드를 실수로 변경하는 것과 관련하여 실제로 문제가되지 않음).

    그러나 g- 힘 계산이 정지되고 대기 속도 계산이 방지되는 계기판의 경우 허용되지 않을 수 있습니다.

일부는 덜 분명합니다.

  • 많은 테스트를 한 경우 첫 번째 주문 결과 (예 : 정답)는 상대적으로 큰 걱정거리가 아닙니다. 당신은 당신의 테스트가 이것을 다룰 것이라는 것을 알고 있습니다. 그러나 숨겨진 상태 또는 제어 흐름이 있었다면 이것이 훨씬 더 미묘한 원인이 아니라는 것을 모릅니다. 이것은 테스트로 배제하기가 어렵습니다.

  • 명백히 안전하다는 것이 상대적으로 중요합니다. 구매하는 출처가 안전한지 아닌지에 대해 많은 고객이 이의를 제기하지는 않습니다. 당신이 다른 한편으로 항공 시장에 있다면 ...

이것이 당신의 예에 어떻게 적용됩니까?

모르겠어요 "중요한 생산 코드에 던지지 않음"과 같은 주요 규칙이 안전상 중요한 코드에 채택되어보다 일반적인 상황에서는 매우 어리석은 사고 프로세스가 많이 있습니다.

일부는 포함, 일부 안전 및 기타에 관련 될 수 있습니다 ... 일부는 양호 (성능 / 메모리 한계가 필요함) 일부는 나쁩니다 (예외를 제대로 처리하지 않으므로 위험을 방지하지 않는 것이 가장 좋습니다). 대부분의 경우 그들이 왜 그랬는지 아는 것조차도 실제로 그 질문에 대답하지 않을 것입니다. 예를 들어 코드를 실제로 감사하는 것보다 더 쉽게 감사하는 것과 관련이 있다면 좋은 습관입니까? 당신은 정말 말할 수 없습니다. 그들은 다른 동물이므로 다르게 취급해야합니다.

말했다 모두, 그것은 나에게 조금의 의심을 보이는 하지만 :

소프트웨어 엔지니어링 스택 교환에 대해 낯선 사람이 안전에 중요한 소프트웨어 및 소프트웨어 설계 결정을 내려서는 안됩니다. 시스템이 불량한 경우에도이를 수행해야 할 충분한 이유가있을 수 있습니다. "생각할 수있는 음식"이외의 다른 내용을 많이 읽지 마십시오.


7

때로는 예외를 던지는 것이 최선의 방법이 아닙니다. 스택 풀림으로 인한 것이 아니라, 특히 언어 또는 인터페이스 이음새와 같이 예외를 포착하는 것이 문제가되기도합니다.

이를 처리하는 좋은 방법은 풍부한 데이터 형식을 반환하는 것입니다. 이 데이터 유형에는 모든 행복한 경로와 모든 불행한 경로를 설명하기에 충분한 상태가 있습니다. 요점은이 함수 (멤버 / 글로벌 / 기타)와 상호 작용하면 결과를 처리해야한다는 것입니다.

이 풍부한 데이터 유형은 행동을 강요해서는 안됩니다. 귀하의 지역 예에서와 같은 것을 상상해보십시오 var area_calc = new AreaCalculator(); var volume = area_calc.CalculateArea(x, y) * z;. 유용한 volume것은 깊이와 곱한 면적을 포함해야합니다. 큐브, 원통 등일 수 있습니다.

그러나 area_calc 서비스가 다운 된 경우 어떻게해야합니까? 그런 다음 area_calc .CalculateArea(x, y)오류가 포함 된 리치 데이터 유형을 리턴했습니다. 그것을 곱하는 것이 합법적 z입니까? 좋은 질문입니다. 사용자가 검사를 즉시 처리하도록 할 수 있습니다. 그러나 이것은 오류 처리로 논리를 손상시킵니다.

var area_calc = new AreaCalculator();
var area_result = area_calc.CalculateArea(x, y);
if (area_result.bad())
{
    //handle unhappy path
}
var volume = area_result.value() * z;

vs

var area_calc = new AreaCalculator();
var volume = area_calc.CalculateArea(x, y) * z;
if (volume.bad())
{
    //handle unhappy path
}

본질적으로 논리는 두 줄에 걸쳐 퍼지고 첫 번째 경우에는 오류 처리로 나뉘며 두 번째 경우는 한 줄의 모든 관련 논리와 오류 처리가 있습니다.

두 번째 경우 volume에는 풍부한 데이터 유형입니다. 단순한 숫자가 아닙니다. 이로 인해 스토리지가 더 커지고 volume오류 상태에 대해 여전히 조사해야합니다. 또한 volume사용자가 오류를 처리하도록 선택하기 전에 다른 계산을 제공하여 여러 다른 위치에 나타날 수 있습니다. 상황의 특성에 따라 좋거나 나쁠 수 있습니다.

또는 volume일반 데이터 유형일 수도 있고 숫자 일 수도 있지만 오류 조건은 어떻게됩니까? 값이 만족스러운 경우 값이 암시 적으로 변환 될 수 있습니다. 불행한 상태이면 기본값 / 오류 값을 반환 할 수 있습니다 (영역 0 또는 -1의 경우 합리적으로 보일 수 있음). 또는 인터페이스 / 언어 경계의이면에서 예외가 발생할 수 있습니다.

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}
var volume = foo();
if (volume <= 0)
{
    //handle error
}

vs.

... foo() {
   var area_calc = new AreaCalculator();
   return area_calc.CalculateArea(x, y) * z;
}

try { var volume = foo(); }
catch(...)
{
    //handle error
}

나쁜 값 또는 나쁜 값을 전달함으로써 사용자에게 데이터 유효성을 검사하는 데 많은 부담을줍니다. 컴파일러에 관한 한 반환 값은 합법적 인 정수이기 때문에 버그의 원인입니다. 무언가를 확인하지 않으면 문제가 발생했을 때 발견됩니다. 두 번째 사례는 예외가 불행한 길을 처리하도록 허용함으로써 행복한 세계가 정상적인 처리를 따르는 것을 허용함으로써 두 세계의 최고를 혼합합니다. 불행히도 사용자가 예외를 현명하게 처리하도록 강요하기는 어렵습니다.

불행한 길을 분명히하는 것은 비즈니스 논리 (예외의 영역)에 알려지지 않은 경우입니다. 비즈니스 규칙 (규칙의 영역)으로 처리하는 방법을 알고 있기 때문에 유효성 검사에 실패하면 행복한 길입니다.

궁극적 인 솔루션은 모든 시나리오를 가능하게하는 솔루션입니다.

  • 사용자는 나쁜 상태를 쿼리하여 즉시 처리 할 수 ​​있어야합니다.
  • 사용자는 행복한 경로를 따른 것처럼 풍부한 유형을 조작하고 오류 세부 사항을 전파 할 수 있어야합니다.
  • 사용자는 캐스팅을 통해 행복한 경로 값을 추출 할 수 있어야하며 (합리적인 내재적 / 명시 적) 불행한 경로에 대한 예외를 생성해야합니다.
  • 사용자는 행복한 경로 값을 추출하거나 기본값 (제공 여부)을 사용할 수 있어야합니다.

다음과 같은 것 :

Rich::value_type value_or_default(Rich&, Rich::value_type default_value = ...);
bool bad(Rich&);
...unhappy path report... bad_state(Rich&);
Rich& assert_not_bad(Rich&);
class Rich
{
public:
   typedef ... value_type;

   operator value_type() { assert_not_bad(*this); return ...value...; }
   operator X(...) { if (bad(*this)) return ...propagate badness to new value...; /*operate and generate new value*/; }
}

//check
if (bad(x))
{
    var report = bad_state(x);
    //handle error
}

//rethrow
assert_not_bad(x);
var result = (assert_not_bad(x) + 23) / 45;

//propogate
var y = x * 23;

//implicit throw
Rich::value_type val = x;
var val = ((Rich::value_type)x) + 34;
var val2 = static_cast<Rich::value_type>(x) % 3;

//default value
var defaulted = value_or_default(x);
var defaulted_to = value_or_default(x, 55);

@TobySpeight Fair, 이것들은 상황에 민감하며 범위가 있습니다.
Kain0_0

여기서 문제는 'assert_not_bad'블록이라고 생각합니다. 나는 이것이 원래의 코드가 해결하려고했던 것과 같은 장소에서 끝나게 될 것이라고 생각합니다. 테스트에서 이러한 사항을 확인해야하지만 실제로 주장하는 경우 실제 항공기에서 생산하기 전에 제거해야합니다. 그렇지 않으면 몇 가지 좋은 점이 있습니다.
drjpizzle

@drjpizzle 나는 테스트를 위해 가드를 추가하는 것이 중요하다면 프로덕션 환경에서 가드를 제자리에 두는 것이 중요하다고 주장한다. 경비원 자체의 존재는 의심을 암시합니다. 테스트하는 동안 코드를 보호하기에 충분한 코드를 의심한다면 기술적 인 이유로 코드를 의심하는 것입니다. 즉, 조건이 현실적으로 발생할 수 있습니다. 테스트를 실행한다고해서 프로덕션 환경에서이 조건에 도달 할 수 없다는 것을 증명할 수는 없습니다. 즉, 어딘가에서 처리해야하는 알려진 상태가있을 수 있습니다. 어떻게 처리되는지 문제라고 생각합니다.
Kain0_0

3

C ++의 관점에서 대답하겠습니다. 모든 핵심 개념을 C #으로 전송할 수 있다고 확신합니다.

선호하는 스타일이 "항상 예외 처리"인 것 같습니다.

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

예외 처리가 무겁기 때문에 C ++ 코드에서 문제가 될 수 있습니다. 실패 사례가 느리게 실행되고 실패 사례가 메모리를 할당하고 (사용할 수없는 경우도 있음) 일반적으로 예측 가능성이 떨어집니다. EH의 헤비급은 "제어 흐름에 예외를 사용하지 마십시오"와 같은 사람들의 말을 듣는 이유 중 하나입니다.

따라서 일부 라이브러리 (예 <filesystem>:)는 C ++에서 "이중 API"라고 부르는 것 또는 C #에서 Try-Parse패턴을 부르는 것을 사용합니다 ( Peter 에게 팁을 주셔서 감사 합니다!)

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        throw Exception("negative side lengths");
    }
    return x * y;
}

bool TryCalculateArea(int x, int y, int& result) {
    if (x < 0 || y < 0) {
        return false;
    }
    result = x * y;
    return true;
}

int a1 = CalculateArea(x, y);
int a2;
if (TryCalculateArea(x, y, a2)) {
    // use a2
}

"이중 API"의 문제점을 즉시 확인할 수 있습니다. 많은 코드 복제, 어떤 API가 "올바른"API인지에 대한 사용자 지침이 없으며 사용자는 유용한 오류 메시지 ( CalculateArea)와 속도 ( TryCalculateArea) 빠른 버전은 우리의 도움이 필요하기 때문에 "negative side lengths"예외를하고에 그것을 아래로 평평하게 쓸모없는 false- "문제가 발생했습니다, 나 무엇이나 물어 보지 않는다"고 말했다. (일부 듀얼 API는 같은 더 표현 오류 유형을 사용하여 int errnoC ++의 나 std::error_code,하지만 여전히 당신을 말하지 않는 경우 오류가 발생 - 그것은 단지 곳 발생합니다.)

코드 작동 방식을 결정할 수없는 경우 언제든지 발신자에게 결정을 내릴 수 있습니다!

template<class F>
int CalculateArea(int x, int y, F errorCallback) {
    if (x < 0 || y < 0) {
        return errorCallback(x, y, "negative side lengths");
    }
    return x * y;
}

int a1 = CalculateArea(x, y, [](auto...) { return 0; });
int a2 = CalculateArea(x, y, [](int, int, auto msg) { throw Exception(msg); });
int a3 = CalculateArea(x, y, [](int, int, auto) { return x * y; });

이것은 본질적으로 동료가하는 일입니다. "오류 처리기"를 전역 변수에 포함시키는 것을 제외하고는 :

std::function<int(const char *)> g_errorCallback;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorCallback("negative side lengths");
    }
    return x * y;
}

g_errorCallback = [](auto) { return 0; };
int a1 = CalculateArea(x, y);
g_errorCallback = [](const char *msg) { throw Exception(msg); };
int a2 = CalculateArea(x, y);

명시 적 함수 매개 변수 에서 전역 상태 로 중요한 매개 변수를 이동하는 것은 거의 항상 나쁜 생각입니다. 나는 그것을 권장하지 않습니다. ( 귀하의 경우 글로벌 상태가 아니라 단순히 인스턴스 전체 멤버 상태 라는 사실 은 악의를 약간 완화하지만 많이는 아닙니다.)

또한 동료가 불필요하게 오류 처리 동작의 수를 제한하고 있습니다. 그는 오류 처리 람다를 허용 하지 않고 두 가지만 결정했습니다.

bool g_errorViaException;

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
        return g_errorViaException ? throw Exception("negative side lengths") : 0;
    }
    return x * y;
}

g_errorViaException = false;
int a1 = CalculateArea(x, y);
g_errorViaException = true;
int a2 = CalculateArea(x, y);

이것은 아마도 이러한 가능한 전략 중 "사워 스팟"일 것입니다. 정확히 두 가지 오류 처리 콜백 중 하나를 사용하도록하여 최종 사용자로부터 모든 유연성을 확보했습니다 . 그리고 당신은 공유 된 세계 국가의 모든 문제를 가지고 있습니다; 그리고 당신은 여전히 ​​모든 조건부 지점에 대한 비용을 지불하고 있습니다.

마지막으로 C ++ (또는 조건부 컴파일 기능이있는 모든 언어)의 일반적인 솔루션은 사용자가 컴파일 타임에 전 세계적으로 전체 프로그램을 결정하도록하여 취하지 않은 코드 경로를 완전히 최적화 할 수 있도록하는 것입니다.

int CalculateArea(int x, int y) {
    if (x < 0 || y < 0) {
#ifdef NEXCEPTIONS
        return 0;
#else
        throw Exception("negative side lengths");
#endif
    }
    return x * y;
}

// Now these two function calls *must* have the same behavior,
// which is a nice property for a program to have.
// Improves understandability.
//
int a1 = CalculateArea(x, y);
int a2 = CalculateArea(x, y);

이런 식으로 작동하는 것의 예는 C와 C ++ 의 assert매크로 로, 전 처리기 매크로에서 동작을 조절합니다 NDEBUG.


대신 std::optionalfrom을 반환하면 TryCalculateArea()컴파일 타임 플래그가있는 단일 함수 템플릿에서 듀얼 인터페이스의 두 부분 구현을 통합하는 것이 간단합니다.
중복 제거기

@ 중복 제거기 : 아마도 std::expected. 단지와 함께 std::optional내가 제안 된 솔루션을 오해하지 않는 한, 그것은 여전히 내가 말한 고통 것 : 사용자가 유용한 오류 메시지 및 속도의 하드 선택을해야합니다 빠른 버전은 우리의 도움이 필요하기 때문에 "negative side lengths"예외를하고 쓸모으로 그것을 아래로 평평하게 false- " 문제가 발생했습니다. 어디에서 무엇을 묻지 마십시오. "
Quuxplusone

그렇기 때문에 libc ++ <filesystem>은 실제로 OP의 동료 패턴과 매우 비슷한 작업을 수행합니다. std::error_code *ec모든 API 레벨을 통해 파이프를 연결 한 다음 맨 아래에있는 것과 같은 도덕적 역할을 if (ec == nullptr) throw something; else *ec = some error code합니다. (실제로 if라는 것을 추상화 ErrorHandler하지만 같은 기본 아이디어입니다.)
Quuxplusone

그것은 확장 오류 정보를 던지지 않고 유지하는 옵션 일 것입니다. 적절하거나 추가 비용이 들지 않을 수 있습니다.
중복 제거기

1
이 답변에 포함 된 많은 좋은 생각 ... 확실히 더 많은
공감대가

1

동료가 어디에서 패턴을 얻었는지 언급해야한다고 생각합니다.

요즘 C #에는 TryGet 패턴이 public bool TryThing(out result)있습니다. 이를 통해 결과를 얻을 수 있으며 결과가 유효한 값인지 계속 알 수 있습니다. 예를 들어 모든 int값은에 대한 유효한 결과 Math.sum(int, int)이지만 값이 오버플로 된 경우이 특정 결과는 가비지가 될 수 있습니다. 이것은 비교적 새로운 패턴입니다.

out키워드를 사용 하기 전에 예외를 발생시켜야하고 (비싸고, 호출자가 예외를 잡거나 전체 프로그램을 종료해야 함), 각 결과에 대해 특수한 구조체 (클래스 또는 제네릭이 실제로 존재했던 클래스)를 작성하여 값을 나타냅니다. 및 가능한 오류 (소프트웨어 제작 및 팽창에 시간이 걸리는) 또는 기본 "오류"값 (오류가 아닐 수도 있음)을 반환합니다.

동료가 사용하는 접근 방식은 새로운 기능을 테스트 / 디버깅하는 동안 예외를 조기에 해결하는 동시에 기본 오류 값을 반환하는 런타임 안전성과 성능 (~ 30 년 전 항상 중요한 문제였습니다)을 제공합니다. 이제 이것은 소프트웨어가 쓰여진 패턴이며, 예상되는 패턴은 앞으로 나아갈 것이므로 지금은 더 좋은 방법이 있더라도 계속 이렇게하는 것이 당연합니다. 이 패턴은 소프트웨어 시대부터 상속되었을 가능성이 높거나 대학에서 결코 자랐던 패턴이 아닙니다 (오래된 습관은 깨지기 어렵습니다).

다른 답변은 이미 이것이 나쁜 습관으로 간주되는 이유를 다루므로 TryGet 패턴을 읽도록 권장하는 것으로 끝날 것입니다 (객체가 호출자에게 약속해야 할 사항을 캡슐화 할 수도 있음).


out키워드 전에 bool결과에 대한 포인터 (예 : ref매개 변수) 를 취하는 함수를 작성해야 합니다 . 1998 년 VB6에서이 작업을 수행 할 수 있습니다. out키워드는 단순히 함수가 반환 될 때 매개 변수가 할당되는 컴파일 타임의 확실성을 구매하기 만하면됩니다. 그것은 이다 비록 좋은 및 유용한 패턴.
Mathieu Guindon

@MathieuGuindon Yeay, 그러나 GetTry는 아직 잘 알려진 / 확립 된 패턴이 아니며, 패턴이 사용 되었더라도 완전히 사용되지는 않았습니다. 결국, Y2K까지의 리드 중 일부는 0-99보다 큰 것을 저장하는 것은 받아 들일 수 없다는 것입니다.
테 즈라

0

당신이 그의 접근을하고 싶을 때가 있지만, 나는 그것들이 "정상적인"상황이라고 생각하지 않을 것입니다. 귀하의 사례를 결정하는 열쇠는 다음과 같습니다.

그의 논리 / 추론은 우리 프로그램이 한 가지 일을해야하며 사용자에게 데이터를 보여야한다는 것입니다. 우리를 방해하지 않는 다른 예외는 무시해야합니다.

요구 사항을 확인하십시오. 요구 사항에 실제로 사용자에게 데이터를 표시하는 하나의 작업이 있다고 말하면 그는 맞습니다. 그러나 내 경험상 사용자 대부분 표시되는 데이터를 관리합니다. 그들은 올바른 데이터를 원합니다. 일부 시스템은 조용히 실패하고 사용자가 문제가 있음을 파악하도록하고 싶지만 규칙의 예외로 간주합니다.

내가 실패한 후 물어볼 주요 질문은 "사용자의 기대와 소프트웨어의 불변이 유효한 상태에있는 시스템입니까?"입니다. 그렇다면, 꼭 돌아와서 계속 가십시오. 실제로 이것은 대부분의 프로그램에서 발생하지 않습니다.

플래그 자체의 경우 예외 플래그는 일반적으로 코드 냄새로 간주됩니다. 사용자가 함수의 작동 방식을 이해하기 위해 모듈이 어떤 모드인지 알아야합니다. 이 글은 만약 !shouldThrowExceptions모드, 사용자는 것을 알 필요 오류를 감지하고 오류가 발생할 때 기대와 불변을 유지하기위한 책임이 있습니다. 또한 함수가 호출되는 라인에서 올바른 책임을집니다. 이와 같은 플래그는 일반적으로 매우 혼동됩니다.

그러나 발생합니다. 많은 프로세서가 프로그램 내에서 부동 소수점 동작 변경을 허용한다고 생각하십시오. 보다 편안한 표준을 원하는 프로그램은 레지스터 (실제로는 플래그)를 변경하여 간단하게 수행 할 수 있습니다. 비결은 실수로 다른 발가락을 밟지 않도록 매우 조심해야한다는 것입니다. 코드는 종종 현재 플래그를 확인하여 원하는 설정으로 설정하고 조작 한 다음 다시 설정합니다. 그렇게하면 아무도 변화에 놀라지 않습니다.


0

이 특정 예제에는 규칙에 영향을 줄 수있는 흥미로운 기능이 있습니다.

CalculateArea(int x, int y)
{
    if(x < 0 || y < 0)
    {
        if(shouldThrowExceptions) 
            throwException;
        else
            return 0;
    }
}

여기에 보이는 것은 전제 조건 점검입니다. 사전 조건 검사가 실패하면 호출 스택에서 버그가 더 높아집니다. 따라서 문제는 코드 가 다른 곳에있는 버그를보고하는 책임 됩니까?

여기에 긴장 중 일부는이 인터페이스를 나타낸다는 사실에 기인 원시적 강박 관념을 - x그리고 y아마도 길이의 실제 측정치로되어있다. 도메인 특정 유형이 합리적인 선택 인 프로그래밍 컨텍스트에서 사실상 전제 조건 확인을 데이터 소스에 더 가깝게 이동합니다. 즉, 데이터 무결성에 대한 책임을 부릅니다. 상황에 대한 더 나은 감각.

즉, 실패한 검사를 관리하기위한 두 가지 다른 전략을 갖는 데 근본적으로 잘못된 것이 없습니다. 내가 선호하는 전략을 결정하기 위해 구성을 사용하는 것이 좋습니다. 기능 플래그는 라이브러리 메소드 구현이 아닌 컴포지션 루트에서 사용됩니다.

// Configurable dependencies
AreaCalculator(PreconditionFailureStrategy strategy)

CalculateArea(int x, int y)
{
    if (x < 0 || y < 0) {
        return this.strategy.fail(0);
    }
    // ...
}

그들은 시스템이 다운 될 수없는 항공을 다루는 중요한 애플리케이션에서 작업했습니다.

국가 교통 안전위원회는 정말 좋습니다. 나는 회색 수염에 대안적인 구현 기술을 제안 할 수는 있지만 오류보고 하위 시스템에서 벌크 헤드를 설계하는 것에 대해서는 논쟁하지 않습니다.

더 광범위하게 : 비즈니스 비용은 얼마입니까? 웹 사이트를 중단시키는 것이 생명에 중요한 시스템보다 훨씬 저렴합니다.


나는 여전히 현대화하면서 원하는 유연성을 유지하는 다른 방법의 제안을 좋아합니다.
drjpizzle

-1

메소드는 예외를 처리하거나 처리하지 않습니다. C #과 같은 언어에서는 플래그가 필요하지 않습니다.

public int Method1()
{
  ...code

 return 0;
}

... 코드에 문제가 발생하면 호출자가 해당 예외를 처리해야합니다. 아무도 오류를 처리하지 않으면 프로그램이 종료됩니다.

public int Method1()
{
try {  
...code
}
catch {}
 ...Handle error 
}
return 0;
}

이 경우 ... 코드에 문제가 발생하면 Method1이 문제를 처리하고 프로그램이 진행됩니다.

예외를 처리하는 위치는 귀하에게 달려 있습니다. 확실하게 당신은 잡거나 아무것도하지 않음으로써 그것들을 무시할 수 있습니다. 그러나 귀하가 발생할 것으로 예상되는 특정 유형의 예외 만 무시하도록하겠습니다. exception ex메모리 부족 등의 시스템 예외와 같은 일부 예외는 무시 하지 않기 때문에 ( )를 무시하는 것은 위험합니다.


3
OP가 게시 한 현재 설정은 기꺼이 예외를 던질 지 여부를 결정하는 것과 관련이 있습니다. OP의 코드는 메모리 부족 예외와 같은 것들을 원치 않는 삼키는 것으로 이어지지 않습니다. 예외가 시스템을 중단 시킨다는 주장은 코드베이스 예외를 포착하지 못 하므로 예외를 삼키지 않을 것임을 암시합니다 . OP의 비즈니스 로직에 의해 의도적으로 던져 졌거나 던져지지 않은 것.
Flater

-1

이 접근 방식은 "실패, 실패"철학을 깨뜨립니다.

왜 빨리 실패 하고 싶 습니까?

  • 실패가 빠를수록 실패의 가시적 증상은 실제 실패 원인에 가깝습니다. 이렇게하면 디버깅이 훨씬 쉬워집니다. 가장 좋은 경우 스택 추적의 첫 번째 줄에 오류 줄이 있습니다.
  • 실패가 빠를수록 (그리고 오류를 적절하게 잡을수록) 나머지 프로그램을 혼동 할 가능성은 줄어 듭니다.
  • 실패가 어려워 질수록 (즉, "-1"코드 또는 이와 유사한 것을 반환하는 대신 예외를 던질수록) 호출자가 실제로 오류를 처리하고 잘못된 값으로 계속 작업하지 않을 가능성이 높습니다.

빠르고 열심히 실패 하지 않는 단점 :

  • 눈에 보이는 고장을 피하면 (즉, 모든 것이 괜찮다고 가정하면) 실제 오류를 찾기가 매우 어려워집니다. 예제의 반환 값이 100 영역의 합을 계산하는 일부 루틴의 일부라고 상상해보십시오. 즉, 해당 함수를 100 번 호출하고 반환 값을 합산합니다. 오류를 자동으로 억제 하면 실제 오류가 발생한 위치를 찾을 수있는 방법없습니다 . 다음의 모든 계산은 조용히 잘못 될 것입니다.
  • 실패가 지연되면 (영역에 대해 "-1"과 같은 불가능한 반환 값을 반환하여) 함수 호출자가 신경 쓰지 않고 오류 처리를 잊어 버릴 가능성을 높입니다. 비록 그들이 실패에 대한 정보를 가지고 있지만.

마지막으로 실제 예외 기반 오류 처리는 "대역 외"오류 메시지 또는 개체를 제공 할 수 있다는 이점이 있으며 도메인 코드에 한 줄을 추가하지 않고도 오류 로깅, 경고 등을 쉽게 연결할 수 있습니다. .

따라서 단순한 기술적 이유뿐만 아니라 "시스템"이유도 있기 때문에 빠르게 실패하는 것이 매우 유용합니다.

하루가 끝날 무렵, 예외 처리가 가볍고 매우 안정적인 현재 시대에 힘들고 빠르게 실패하지 않는 것은 절반의 범죄입니다. 예외를 억제하는 것이 좋다는 생각이 어디에서 왔는지 완벽하게 이해하지만 더 이상 적용 할 수 없습니다.

특히 예외가 발생할지 여부에 대한 옵션을 제공하는 특별한 경우에는 호출자가 어쨌든 결정해야 함을 의미합니다 . 따라서 호출자가 예외를 잡아 적절하게 처리하도록하는 데에는 결점이 없습니다.

한 가지 의견이 떠오른다 :

다른 답변에서 실행중인 하드웨어가 비행기 일 때 중요한 응용 프로그램에서는 빠르고 힘든 실패가 바람직하지 않다는 것이 지적되었습니다.

빠르고 힘들게 실패한다고해서 전체 응용 프로그램이 중단되는 것은 아닙니다. 오류가 발생한 지점에서 오류가 발생했음을 의미합니다. OP의 예에서 일부 영역을 계산하는 저수준 방법은 오류를 잘못된 값으로 자동으로 대체해서는 안됩니다. 분명히 실패해야합니다.

체인을 호출하는 일부 발신자는 분명히 해당 오류 / 예외를 포착하여 적절하게 처리해야합니다. 비행기에서이 방법을 사용한 경우 오류 LED가 켜지거나 잘못된 영역 대신 "오류 계산 영역"이 표시 될 수 있습니다.


3
다른 답변에서 실행중인 하드웨어가 비행기 일 때 중요한 응용 프로그램에서는 빠르고 열심히 실패하는 것이 바람직하지 않다는 것이 지적되었습니다.
HAEM

1
@HAEM, 그것은 빠르고 어려운 실패의 의미에 대한 오해입니다. 이에 대한 단락을 답변에 추가했습니다.
AnoE

의도가 '열심히'실패하지 않더라도 여전히 그런 종류의 불을 가지고 노는 것은 위험한 것으로 여겨집니다.
drjpizzle

@drjpizzle, 요점입니다. 당신이 빠른 실패에 실패하는 데 익숙하다면, 그것은 내 경험에서 "위험"하거나 "그런 종류의 불을 가지고 노는"것이 아닙니다. Au contraire. "여기에서 예외가 발생하면 어떻게 될지"에 대해 생각하는 데 익숙 하다는 것을 의미합니다. 여기서 "여기"는 모든 곳을 의미 하며 현재 프로그래밍중인 지점에 중대한 문제가 있는지 여부를 알고 있습니다 ( 그 경우에 비행기 추락). 불을 가지고 노는 것은 모든 것이 대부분 잘 될 것으로 예상하고 모든 구성 요소는 의심 할 여지없이 모든 것이 괜찮다고 가정합니다.
AnoE
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.