[exception] 예외 처리가 나쁜 이유는 무엇입니까?

Google의 Go 언어는 디자인 선택으로 예외가 없으며 Linux 명성의 Linus는 예외를 쓰레기라고 불렀습니다. 왜?



답변

예외는 throw되는 예외가 불변성을 깨고 객체를 일관성없는 상태로 남겨 두는 코드를 작성하는 것을 정말 쉽게 만듭니다. 그들은 본질적으로 당신이 만드는 대부분의 진술이 잠재적으로 던질 수 있다는 것을 기억하고 올바르게 처리하도록 강요합니다. 그렇게하는 것은 까다 롭고 직관적이지 않을 수 있습니다.

다음과 같은 것을 간단한 예로 고려하십시오.

class Frobber
{
    int m_NumberOfFrobs;
    FrobManager m_FrobManager;

public:
    void Frob()
    {
        m_NumberOfFrobs++;

        m_FrobManager.HandleFrob(new FrobObject());
    }
};

가정 FrobManager것이다 오른쪽이 외모 확인을? 아니면 아닐 수도 있습니다 … 그렇다면 또는 예외가 발생하는 경우를 상상해보십시오 . 이 예에서 증분은 롤백되지 않습니다. 따라서이 인스턴스를 사용하는 사람 은 개체가 손상되었을 수 있습니다.deleteFrobObjectFrobManager::HandleFrob()operator newm_NumberOfFrobsFrobber

이 예제는 어리석은 것처럼 보일 수 있지만 (좋아요, 하나를 구성하기 위해 약간의 노력을 기울여야했습니다. :-)),하지만 핵심은 프로그래머가 예외를 계속 생각하지 않고 모든 상태 순열이 롤링되는지 확인하는 것입니다. 던질 때마다 이런 식으로 문제가 생깁니다.

예를 들어 뮤텍스를 생각하는 것처럼 생각할 수 있습니다. 중요한 섹션 내에서 데이터 구조가 손상되지 않았는지, 다른 스레드가 중간 값을 볼 수 없는지 확인하기 위해 여러 명령문에 의존합니다. 이러한 진술 중 하나가 무작위로 실행되지 않으면 고통의 세계에 빠집니다. 이제 잠금과 동시성을 제거하고 각 방법에 대해 그렇게 생각하십시오. 원한다면 각 방법을 객체 상태에 대한 순열 트랜잭션으로 생각하십시오. 메서드 호출을 시작할 때 개체는 깨끗한 상태 여야하고 마지막에는 깨끗한 상태 여야합니다. 그 사이에 변수 foo는 다음과 일치하지 않을 수 있습니다.bar, 그러나 귀하의 코드는 결국 그것을 수정할 것입니다. 예외의 의미는 귀하의 진술 중 하나가 언제든지 귀하를 방해 할 수 있다는 것입니다. 각 개별 메서드에서 문제가 발생하면 문제가 발생하면 롤백하거나 작업을 정렬하여 throw가 개체 상태에 영향을주지 않도록해야합니다. 잘못 이해하면 (그리고 이런 종류의 실수를하기 쉽습니다) 호출자는 결국 중간 값을 보게됩니다.

C ++ 프로그래머가이 문제에 대한 궁극적 인 해결책으로 언급하기를 좋아하는 RAII와 같은 메서드는이를 방지하기 위해 먼 길을갑니다. 그러나 그것들은 은총 알이 아닙니다. 던질 때 리소스를 해제 할 수는 있지만 객체 상태의 손상과 호출자가 중간 값을 보는 것에 대해 생각할 필요가 없습니다. 따라서 많은 사람들에게 코딩 스타일의 명목상 예외없다고 말하는 것이 더 쉽습니다 . 작성하는 코드의 종류를 제한하면 이러한 버그를 도입하기가 더 어렵습니다. 그렇지 않으면 실수하기가 매우 쉽습니다.

C ++의 예외 안전 코딩에 대해 전체 책이 작성되었습니다. 많은 전문가들이 잘못 알고 있습니다. 정말 그렇게 복잡하고 뉘앙스가 너무 많다면 해당 기능을 무시해야한다는 좋은 신호일 수 있습니다. 🙂


답변

Go에 예외가없는 이유는 Go 언어 디자인 FAQ에 설명되어 있습니다.

예외도 비슷한 이야기입니다. 예외에 대한 많은 디자인이 제안되었지만 각각은 언어와 런타임에 상당한 복잡성을 추가합니다. 본질적으로 예외는 함수와 고 루틴에 걸쳐 있습니다. 그들은 광범위한 의미를 가지고 있습니다. 그들이 도서관에 미칠 영향에 대한 우려도 있습니다. 정의상 예외적이지만이를 지원하는 다른 언어에 대한 경험은 라이브러리 및 인터페이스 사양에 큰 영향을 미친다는 것을 보여줍니다. 일반적인 오류를 모든 프로그래머가 보상해야하는 특수 제어 흐름으로 바꾸지 않고 진정으로 예외적 일 수있는 디자인을 찾는 것이 좋을 것입니다.

제네릭과 마찬가지로 예외도 여전히 미해결 문제입니다.

즉, Go에서 만족 스럽다고 생각하는 방식으로 예외를 지원하는 방법을 아직 파악하지 못했습니다. 그들은 예외가 그 자체 로 나쁘다고 말하는 것이 아닙니다 .

업데이트-2012 년 5 월

Go 디자이너는 이제 울타리에서 내려 왔습니다. 그들의 FAQ는 다음과 같이 말합니다.

try-catch-finally 관용구 에서처럼 예외를 제어 구조에 결합하면 코드가 복잡해집니다. 또한 프로그래머가 파일을 열지 못하는 것과 같은 너무 많은 일반적인 오류를 예외적으로 분류하도록 장려하는 경향이 있습니다.

Go는 다른 접근 방식을 취합니다. 일반 오류 처리를 위해 Go의 다중 값 반환을 사용하면 반환 값을 오버로드하지 않고도 오류를 쉽게보고 할 수 있습니다. Go의 다른 기능과 결합 된 표준 오류 유형은 오류 처리를 즐겁게하지만 다른 언어의 오류 처리와는 상당히 다릅니다.

Go에는 또한 진정으로 예외적 인 조건에서 신호를 보내고 복구 할 수있는 몇 가지 내장 기능이 있습니다. 복구 메커니즘은 오류 후 해체되는 함수 상태의 일부로 만 실행됩니다. 이는 재앙을 처리하기에 충분하지만 추가 제어 구조가 필요하지 않으며 잘 사용하면 깨끗한 오류 처리 코드를 생성 할 수 있습니다.

자세한 내용은 지연, 패닉 및 복구 문서를 참조하십시오.

따라서 짧은 대답은 다중 값 반환을 사용하여 다르게 할 수 있다는 것입니다. (그리고 어쨌든 예외 처리의 한 형태가 있습니다.)


… 그리고 Linux 명성의 Linus는 예외를 쓰레기라고 불렀습니다.

Linus가 예외가 쓰레기라고 생각하는 이유를 알고 싶다면 주제에 대한 그의 글을 찾는 것이 가장 좋습니다. 지금까지 내가 추적 한 유일한 것은 C ++대한 두 개의 이메일에 포함 된이 인용문입니다 .

“전체 C ++ 예외 처리는 근본적으로 깨졌습니다. 특히 커널에서 깨졌습니다.”

그는 특히 C ++ 예외에 대해 이야기하고 있으며 일반적인 예외는 아닙니다. (그리고 C ++ 예외 에는 올바르게 사용하기 까다로운 몇 가지 문제가있는 것 같습니다.)

내 결론은 Linus가 예외 (일반적으로)를 전혀 “쓰레기”라고 부르지 않았다는 것입니다!


답변

예외는 그 자체로 나쁘지는 않지만 많이 발생할 것이라는 것을 알고 있다면 성능 측면에서 비용이 많이들 수 있습니다.

경험상 예외는 예외 조건에 플래그를 지정해야하며 프로그램 흐름을 제어하는 ​​데 사용해서는 안된다는 것입니다.


답변

나는 “예외 상황에서만 예외를 던진다”에 동의하지 않는다. 일반적으로 사실이지만 오해의 소지가 있습니다. 예외는 오류 조건 (실행 실패)에 대한 것입니다.

사용하는 언어에 관계없이 Framework Design Guidelines : Conventions, Idioms, Patterns for Reusable .NET Libraries (2nd Edition)를 선택하십시오. 예외 발생에 대한 장은 피어가 없습니다. 초판 (제 2 판)의 일부 인용문 :

  • 오류 코드를 반환 하지 마십시오 .
  • 오류 코드는 쉽게 무시할 수 있으며 종종 무시됩니다.
  • 예외는 프레임 워크에서 오류를보고하는 주요 수단입니다.
  • 경험상 좋은 규칙은 메서드가 이름이 암시하는대로 수행하지 않는 경우 메서드 수준 실패로 간주되어 예외가 발생한다는 것입니다.
  • 가능하면 정상적인 제어 흐름에 예외를 사용 하지 마십시오 .

예외의 이점 (API 일관성, 오류 처리 코드 위치 선택, 견고성 향상 등)에 대한 메모 페이지가 있습니다. 여러 패턴 (Tester-Doer, Try-Parse)을 포함하는 성능 섹션이 있습니다.

예외와 예외 처리는 하지 나쁜. 다른 기능과 마찬가지로 오용 될 수 있습니다.


답변

golang의 관점에서 볼 때 예외 처리가 없으면 컴파일 프로세스가 간단하고 안전하게 유지됩니다.

Linus의 관점에서 나는 커널 코드가 코너 케이스에 관한 것임을 이해합니다. 따라서 예외를 거부하는 것이 합리적입니다.

코드에서 예외는 현재 작업을 바닥에 내려도 괜찮고 일반적인 경우 코드가 오류 처리보다 더 중요하다는 점입니다. 그러나 컴파일러에서 코드 생성이 필요합니다.

예를 들어 웹 및 데스크톱 응용 프로그램 코드와 같은 대부분의 고급 사용자 용 코드에서는 문제가 없습니다.


답변

예외는 그 자체로 “나쁜”것이 아니라 때때로 나쁜 경향이있는 예외가 처리되는 방식입니다. 이러한 문제 중 일부를 완화하기 위해 예외를 처리 할 때 적용 할 수있는 몇 가지 지침이 있습니다. 이들 중 일부는 다음을 포함합니다 (하지만 이에 국한되지 않음).

  1. 프로그램 흐름을 제어하기 위해 예외를 사용하지 마십시오. 즉, “catch”문에 의존하여 논리 흐름을 변경하지 마십시오. 이것은 로직 주변의 다양한 세부 사항을 숨기는 경향이있을뿐만 아니라 성능 저하로 이어질 수 있습니다.
  2. 반환 된 “상태”가 더 합리적 일 때 함수 내에서 예외를 throw하지 마십시오. 예외적 인 상황에서만 예외를 throw하십시오. 예외를 만드는 것은 비용이 많이 들고 성능 집약적 인 작업입니다. 예를 들어, 파일을 열기 위해 메서드를 호출했는데 해당 파일이 존재하지 않는 경우 “FileNotFound”예외가 발생합니다. 고객 계정이 있는지 확인하는 메서드를 호출하는 경우 부울 값을 반환하고 “CustomerNotFound”예외는 반환하지 마십시오.
  3. 예외 처리 여부를 결정할 때 예외에 대해 유용한 작업을 수행 할 수없는 경우 “try … catch”절을 사용하지 마십시오. 예외를 처리 할 수없는 경우 호출 스택을 버블 링하도록해야합니다. 그렇지 않으면 예외가 처리기에 의해 “삼켜 질”수 있고 세부 정보가 손실됩니다 (예외를 다시 던지지 않는 한).


답변

일반적인 주장은 특정 코드에서 어떤 예외가 나올지 (언어에 따라 다름), gotos 와 너무 비슷 하여 정신적으로 실행을 추적하기 어렵게 만드는 방법 이 없다는 것 입니다.

http://www.joelonsoftware.com/items/2003/10/13.html

이 문제에 대한 합의는 확실히 없습니다. Linus와 같은 하드 코어 C 프로그래머의 관점에서 예외는 확실히 나쁜 생각이라고 말할 수 있습니다. 하지만 일반적인 자바 프로그래머는 매우 다른 상황에 처해 있습니다.