exception - 종류 - 예외처리 영어로




규칙적인 제어 흐름으로 예외를 사용하지 않는 이유는 무엇입니까? (16)

인터넷으로 검색 할 수있는 모든 표준 응답을 피하기 위해 모든 사용자가 자유롭게 공격 할 수있는 예를 제공 할 것입니다.

C #과 자바 (그리고 너무 많은 다른 것들)는 내가 좋아하지 않는 '오버 플로우'행동을 많이 가지고있다. (예. type.MaxValue + type.SmallestValue == type.MinValue : int.MaxValue + 1 == int.MinValue ).

그러나 내 악의적 인 본성을 보았을 때이 동작을 확장하여이 부상에 대한 모욕을 더할 것입니다. 재정의 된 DateTime 유형을 예로 들어 보겠습니다. ( DateTime 은 .NET에서 봉인되어 있지만이 예제에서는 DateTime이 봉인되지 않았다는 사실을 제외하고는 C #과 비슷한 의사 언어를 사용하고 있습니다.)

오버라이드 (override) 된 Add 메소드 :

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

물론 if는 이것을 쉽게 해결할 수 있지만 사실은 왜 예외를 사용할 수 없는지를 볼 수 없다는 것입니다 (논리적으로 말하면, 성능이 특정 상황에서 예외를 피해야 할 때 볼 수 있습니다. ).

나는 많은 경우에 그것들이 if 구조보다 더 명확하고 방법이 만드는 어떤 계약도 깨지 않는다고 생각한다.

IMHO는 "규칙적인 프로그램 흐름을 위해 절대 사용하지 마십시오"라는 반응은 모든 사람이 가지고있는 것처럼 보입니다.

또는 나는 틀린가?

나는 모든 종류의 특별한 경우를 다루는 다른 게시물을 읽었지만, 내 포인트는 당신이 둘 다 있다면 아무 문제가 없다는 것이다.

  1. 명확한
  2. 귀하의 방법에 대한 계약을 존중하십시오.

날 쏴.


C에서 스택 프레임의 유사한 언 롤링을 수행하는 데 사용할 수있는 setjmplongjmp 가 예외 이전에는 발생했습니다.

그런 다음 동일한 구성에 이름이 지정되었습니다 : "예외". 그리고 대부분의 답변은이 이름의 의미에 의존하여 사용법을 논합니다. 예외적 인 조건에 사용하기위한 것입니다. 그건 원래의 longjmp 에는 반영되지 않았습니다. 많은 스택 프레임에서 제어 흐름을 중단해야하는 상황이있었습니다.

예외는 동일한 스택 프레임 내에서 예외를 사용할 수 있다는 점에서 약간 더 일반적입니다. 이것은 잘못된 것이라고 생각하는 goto 와의 유추를 불러옵니다. Gotos는 단단히 결합 된 쌍입니다 (그리고 setjmplongjmp 마찬가지입니다) 예외는 느슨하게 결합 된 publish / subscribe를 따르므로 훨씬 더 깨끗합니다!

세 번째 혼란의 원천은 체크 여부 또는 체크되지 않은 예외와 관련이 있습니다. 물론 제어되지 않는 예외는 제어 흐름에 사용하는 것이 특히 끔찍한 것처럼 보입니다.

확인 된 예외는 제어 흐름에 좋지만, 일단 모든 빅토리아 여왕의 장애를 극복하고 약간만 살아야합니다.

필자가 가장 좋아하는 사용법은 찾고자하는 것을 찾을 때까지 한 가지를 시도하는 코드의 긴 조각에서 throw new Success()throw new Success() 의 순서입니다. 논리의 각 조각마다 각기 다른 모양의 둥지를 만들 수 있으므로 모든 종류의 상태 테스트와 마찬가지로 break 도 단축됩니다. if-else 패턴은 부서지기 쉽습니다. else 편집하거나 다른 방법으로 구문을 엉망으로 만들면 털이 많은 버그가 생깁니다.

throw new Success() 사용하면 코드 흐름이 선형화 됩니다. 저는 로컬에서 정의 된 Success 클래스를 사용합니다 - 물론 확인했습니다. 그래서 catch하는 것을 잊어 버리면 코드가 컴파일되지 않습니다. 그리고 저는 다른 방법의 Success 포착하지 않습니다.

때로는 내 코드가 다른 코드를 검사하고 모든 것이 정상이면 성공합니다. 이 경우에는 throw new Failure() 선형 throw new Failure() 사용하여 비슷한 선형화를 수행합니다.

별도의 기능을 사용하면 구획화의 자연스러운 수준을 망칠 수 있습니다. 따라서 return 솔루션이 최적이 아닙니다. 나는인지 적 이유로 한 곳에서 한 페이지 또는 두 개의 코드를 갖는 것을 선호합니다. 나는 초 미세 코드를 믿지 않는다.

JVM 또는 컴파일러가 수행하는 작업은 핫스팟이 없으면 나와 관련성이 적습니다. 컴파일러가 국지적으로 예외를 탐지하지 않고 예외를 포착하지 못하게하고 기계 코드 레벨에서 매우 효율적인 goto 로 취급하는 근본적인 이유가 있음을 믿을 수 없습니다.

예외 흐름이 아닌 일반적인 경우와 같이 제어 흐름을 위해 함수를 사용하는 한, 여러 번의 중단, 조건 테스트보다 효율성이 떨어지는 것을 알 수는 없지만 단지 복원하는 것과는 대조적으로 3 개의 스택 프레임을 통해 복귀합니다 스택 포인터.

필자는 개인적으로 스택 프레임에서 패턴을 사용하지 않고 어떻게 우아하게 디자인 할 필요가 있는지 알 수 있습니다. 그러나 조심스럽게 사용하면 좋을 것입니다.

마지막으로, 놀라운 처녀 프로그래머와 관련해서는 설득력있는 이유는 아닙니다. 당신이 부드럽게 연습에 그들을 소개한다면, 그들은 그것을 사랑하는 법을 배울 것입니다. 나는 C 프로그래머들에게 놀라움과 겁을주기 위해 사용 된 C ++를 기억합니다.


Josh Bloch는 Effective Java에서이 주제를 광범위하게 다룹니다. 그의 제안은 밝게 빛나고 있으며 .Net에도 적용해야합니다 (세부 정보 제외).

특히 예외적 인 경우에는 예외를 사용해야합니다. 그 이유는 주로 유용성과 관련이 있습니다. 주어진 방법을 최대로 사용할 수 있으려면 입력 및 출력 조건을 최대로 제한해야합니다.

예를 들어 두 번째 방법은 첫 번째 방법보다 사용하기 쉽습니다.

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 * @throws AdditionException if addend1 or addend2 is less than or equal to zero
 */
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
  if( addend1 <= 0 ){
     throw new AdditionException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new AdditionException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

/**
 * Adds two positive numbers.
 *
 * @param addend1 greater than zero
 * @param addend2 greater than zero
 */
public int addPositiveNumbers(int addend1, int addend2) {
  if( addend1 <= 0 ){
     throw new IllegalArgumentException("addend1 is <= 0");
  }
  else if( addend2 <= 0 ){
     throw new IllegalArgumentException("addend2 is <= 0");
  }
  return addend1 + addend2;
}

두 경우 모두 호출자가 API를 적절하게 사용하고 있는지 확인해야합니다. 그러나 두 번째 경우에는 암묵적으로 요구합니다. 유저가 javadoc를 읽지 않았던 경우에도 소프트 예외는 슬로우되지만,

  1. 문서화 할 필요가 없습니다.
  2. 당신은 그것을 시험 할 필요가 없습니다 (당신의 단위 테스트 전략이 얼마나 격렬한가에 달려 있습니다).
  3. 호출자가 세 가지 유스 케이스를 처리 할 필요는 없습니다.

최하위 점은 리턴 코드로 예외를 사용해서는 안되며 주로 API뿐만 아니라 호출자의 API도 복잡하기 때문입니다.

물론 옳은 일을하는 것은 비용으로 발생합니다. 비용은 모든 사람들이 문서를 읽고 따라야한다는 것을 이해해야한다는 것입니다. 어쨌든 그렇게되기를 바랍니다.


나의 엄지 손가락 규칙은 다음과 같습니다.

  • 오류로부터 복구하기 위해 무엇이든 할 수 있다면 예외를 잡아라.
  • 오류가 매우 일반적인 경우 (예 : 사용자가 잘못된 암호로 로그인하려고 시도한 경우), returnvalues
  • 오류에서 복구 할 수 없다면 잡히지 말고 (또는 메인 캐처에서 잡아서 애플리케이션을 반쯤 종료하십시오)

예외로 볼 수있는 문제는 순전히 구문 관점에서 나온 것입니다. (나는 성능상의 오버 헤드가 거의 없다고 확신합니다). 나는 그 곳 곳곳에 try-blocks을 좋아하지 않는다.

이 예제를 보자.

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

.. 또 다른 예는 팩토리를 사용하여 핸들에 무언가를 할당해야하고 팩토리가 예외를 throw 할 수있는 경우 일 수 있습니다.

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

Soo, 개인적으로 나는 드문 오류 조건 (메모리 부족 등)에 대한 예외를 유지하고 오류 값 확인을 위해 returnvalues ​​(valueclasses, structs 또는 enum)를 사용해야한다고 생각합니다.

희망 귀하의 질문을 이해 :)


블로그 게시물에 설명 된 우수 사례는 다음과 같습니다.

  • 소프트웨어에 예기치 않은 상황을 알리는 예외를 던집니다 .
  • 입력 유효성 검증에 리턴 값을 사용하십시오 .
  • 라이브러리가 던진 예외를 처리하는 방법을 알고 있다면 가능한 한 가장 낮은 수준에서 잡으십시오 .
  • 예기치 않은 예외가 있으면 현재 작업을 완전히 버립니다. 당신이 그들을 다루는 방법을 가장하는 척하지 마십시오 .

명시된 이유 외에도 흐름 제어에 예외를 사용하지 않는 한 가지 이유는 디버깅 프로세스를 크게 복잡하게 만들 수 있다는 것입니다.

예를 들어, VS에서 버그를 추적하려고 할 때 일반적으로 "모든 예외에서 중단"을 설정합니다. 흐름 제어에 대한 예외를 사용하는 경우 디버거를 정기적으로 중단해야하며 실제 문제가 발생할 때까지 이러한 비 예외 예외를 무시하지 않아야합니다. 이것은 누군가를 미치게 할 것 같다!


어때? .NET 웹 응용 프로그램을로드 테스트하는 동안 일반적으로 발생하는 예외가 수정되고 그 수가 500 명으로 늘어날 때까지 웹 서버 당 100 명의 시뮬레이션 사용자를 선발했습니다.


인용 한 코드에서 프로그램 흐름을 어떻게 제어하는지 실제로 알 수 없습니다. ArgumentOutOfRange 예외 외에도 다른 예외는 절대로 볼 수 없습니다. (따라서 두 번째 catch 절은 절대 적중되지 않습니다.) if 문을 모방하는 데 드는 비용은 매우 비쌉니다.

또한 당신은 흐름 제어를 수행하기 위해 어딘가에 잡히기 위해 순전히 예외를 던지는 운영의 더 불길한 행동을하지 않습니다. 예외적 인 경우를 실제로 처리하고 있습니다.


일반적으로 낮은 수준에서 예외를 처리하는 것은 본질적으로 잘못된 것은 아닙니다. 예외는 작업을 수행 할 수없는 이유에 대한 자세한 정보를 제공하는 유효한 메시지입니다. 그리고 만약 당신이 그것을 다룰 수 있다면,해야합니다.

일반적으로 당신이 확인할 수있는 실패 확률이 높다는 것을 안다면 체크를해야합니다 ... if (obj! = null) obj.method ()

귀하의 경우에는 날짜 시간에 타임 스탬프가 범위를 벗어 났는지 여부를 쉽게 확인할 수 있는지 알기 위해 C # 라이브러리에 익숙하지 않습니다. 그렇다면 if (.isvalid (ts))를 호출하십시오. 그렇지 않으면 코드가 기본적으로 잘됩니다.

그래서, 기본적으로 더 깨끗한 코드를 생성하는 방법 중 하나가 될 것입니다 ... 예상 된 예외를 방지하기위한 연산이 예외 처리보다 복잡하다면; 어디서나 복잡한 경비원을 만드는 대신 예외를 처리 할 수있는 권한이 있습니다.


정상적인 작동 과정에서 초당 5 번의 예외를 발생시키는 프로그램을 디버깅하려 한 적이 있습니까?

나는 가지고있다.

프로그램은 매우 복잡했습니다 (분산 컴퓨팅 서버였습니다). 프로그램의 한 쪽에서 약간 수정하면 완전히 다른 곳에서 무언가를 쉽게 깨뜨릴 수있었습니다.

나는 단지 프로그램을 시작하고 예외가 발생할 때까지 기다릴 수 있었으면 좋겠지 만 정상적인 운영 과정에서 시작하는 동안 약 200 건의 예외가있었습니다.

내 요점 : 일반적인 상황에 대한 예외를 사용하는 경우 비정상적인 (즉, 예외적 인 ) 상황을 어떻게 파악합니까?

물론 예외를 너무 많이 사용하지 않는 다른 강력한 이유가 있습니다. 특히 성능 측면에서 그렇습니다.


제어 흐름에 대한 예외를 사용할 수있는 것처럼 망치의 ​​발톱을 사용하여 나사를 돌릴 수 있습니다. 그렇다고이 기능을 의도 한 용도로 사용 하는 것은 아닙니다. if 문은 의도 된 사용 흐름을 제어하는 ​​조건을 나타냅니다.

의도하지 않은 방식으로 기능을 사용하면서 해당 기능을 사용하지 않을 경우 관련 비용이 발생합니다. 이 경우 실제 부가 가치가없는 경우 선명도와 성능이 저하됩니다. 널리 사용되는 if 문을 통해 예외를 사용하면 무엇을 구매할 수 있습니까?

다른 말로하면 : 할 있다고해서 네가 해야 한다는 뜻은 아니다.


코드를 읽기가 어렵 기 때문에 디버깅하는 데 어려움을 겪을 수 있습니다. 오랜 시간이 지난 후에 버그를 수정할 때 새로운 버그를 소개하고, 리소스와 시간면에서 비용이 많이 들며, 코드를 디버깅 할 때 귀찮아합니다. 디버거는 모든 예외 발생시 중단됩니다.


표준 anwser는 예외가 정기적이지 않으며 예외적 인 경우에 사용해야한다는 것입니다.

나에게 중요한 이유 중 하나는 유지 또는 디버깅하는 소프트웨어에서 try-catch 제어 구조를 읽을 때 원래의 코더가 if-else 구조 대신 예외 처리를 사용하는 이유를 찾아 내려고합니다. 그리고 저는 좋은 대답을 찾을 것으로 기대합니다.

컴퓨터뿐만 아니라 다른 코더에 대해서도 코드를 작성해야한다는 것을 기억하십시오. 기계가 신경 쓰지 않기 때문에 버려 둘 수없는 예외 처리기와 관련된 의미 체계가 있습니다.


하나의 언어가 메소드를 통해 값을 반환하지 않고 종료하고 다음 "catch"블록으로 되돌릴 수있는 몇 가지 일반적인 메커니즘이 있습니다.

  • 메소드가 스택 프레임을 검사하여 호출 사이트를 결정하고 호출 사이트의 메타 데이터를 사용 try하여 호출 메소드 내의 블록 에 대한 정보 또는 호출 메소드가 호출자의 주소를 저장 한 위치 를 찾습니다 . 후자의 경우에는 호출자의 호출자가 직접 호출자와 동일한 방식으로 결정할 메타 데이터를 조사하여 try블록을 찾거나 스택이 비어 있을 때까지 반복 합니다. 이 접근법은 예외가 발생하지 않는 경우에 약간의 오버 헤드를 추가하지만 예외가 발생하면 비용이 많이 듭니다.

  • 메소드가 예외로부터의 정상적인 리턴을 구별하는 "숨겨진"플래그를 리턴하도록하고, 호출자가 플래그를 점검하도록 설정하고 "예외"루틴으로 분기하도록하십시오. 이 루틴은 예외가 발생하지 않는 경우 1-2 개의 명령어를 추가하지만 예외가 발생하면 비교적 적은 오버 헤드를 추가합니다.

  • 호출자가 스택 반환 주소에 상대적인 고정 주소에 예외 처리 정보 또는 코드를 배치하도록합니다. 예를 들어 ARM에서는 "BL 서브 루틴"명령을 사용하는 대신 시퀀스를 사용할 수 있습니다.

        adr lr,next_instr
        b subroutine
        b handle_exception
    next_instr:
    

일반적으로 종료하려면, 서브 루틴은 간단 할 것 bx lr또는 pop {pc}; 비정상 종료의 경우 서브 루틴은 반환 또는 사용을 수행하기 전에 LR에서 4를 뺍니다 sub lr,#4,pc(ARM 변형, 실행 모드 등에 따라 다름).이 방법은 호출자가이를 수용하도록 설계되지 않은 경우 매우 잘못 작동합니다.

확인 된 예외를 사용하는 언어 또는 프레임 워크는 위의 # 2 또는 # 3과 같은 메커니즘으로 처리해야하는 이점이 있지만 확인되지 않은 예외는 # 1을 사용하여 처리됩니다. Java에서 확인 된 예외를 구현하는 것은 다소 불편하지만 호출 사이트에서 "이 메서드는 XX를 throw하는 것으로 선언되지만, 나는 예상하지 못합니다"라고 말할 수있는 방법이 있다면 나쁜 개념이 아닙니다. "unchecked"예외로 다시 태어난다. 체크 된 예외가 그런 방식으로 처리되는 프레임 워크에서, 어떤 문맥에서는 a를 가질 수있는 구문 분석 메소드 같은 것들을위한 흐름 제어의 효과적인 수단이 될 수있다. 실패의 가능성은 높지만 실패는 성공보다 근본적으로 다른 정보를 반환해야합니다.그러나 그러한 패턴을 사용하는 모든 프레임 워크를 알지 못합니다. 대신,보다 일반적인 패턴은 모든 예외에 대해 위의 첫 번째 방법 (예외가없는 경우는 최소 비용이지만 예외가 발생하면 높은 비용)을 사용하는 것입니다.


Common Lisp의 조건 시스템을 살펴 보는 것에 관심이 있을지도 모르는데, 이는 일종의 예외 예외의 일반화입니다. 제어 된 방법으로 스택을 풀거나 풀 수 없으므로 "재시작"도 발생하므로 매우 편리합니다.

이 기능은 다른 언어의 모범 사례와는 관련이 없지만 생각하고있는 방향으로 디자인 아이디어를 통해 수행 할 수있는 것을 보여줍니다.

물론 요요처럼 스택을 위아래로 움직이는 경우에는 성능 고려 사항이 있지만 예외 시스템을 구현하는 대부분의 캐치 / 스로우 방식에 대해서는 "보잘 것 없다"라는 접근 방식보다 훨씬 일반적인 아이디어입니다.


나는 흐름 제어를위한 예외를 사용하는 것에 잘못된 것이 있다고 생각하지 않는다. 예외는 연속 및 정적 유형의 언어와 다소 유사하지만 예외는 연속보다 강력하므로 연속을 필요로하지만 언어에 포함되지 않은 경우 예외를 사용하여 예외를 구현할 수 있습니다.

글쎄, 실제로, 당신은 계속성이 필요하고 당신의 언어에는 그것들이 없다면, 당신은 틀린 언어를 선택했고 당신은 오히려 다른 언어를 사용해야 만합니다. 그러나 때로는 선택의 여지가없는 경우가 있습니다. 클라이언트 측 웹 프로그래밍이 가장 좋은 예입니다. 자바 스크립트를 둘러 볼 방법이 없습니다.

예 : Microsoft Volta 는 웹 응용 프로그램을 .NET으로 작성하고 프레임 워크에서 어느 비트를 실행해야하는지 파악할 수있게하는 프로젝트입니다. 결과적으로 Volta는 클라이언트에서 코드를 실행할 수 있도록 CIL을 JavaScript로 컴파일 할 수 있어야합니다. 그러나 문제가 있습니다. .NET에는 다중 스레딩이 있으며 JavaScript는 그렇지 않습니다. 따라서 Volta는 JavaScript Exceptions를 사용하여 JavaScript에서 연속체를 구현 한 다음 해당 연속체를 사용하여 .NET Threads를 구현합니다. 그렇게하면 스레드를 사용하는 Volta 응용 프로그램이 수정되지 않은 브라우저에서 실행되도록 컴파일 될 수 있습니다. Silverlight는 필요하지 않습니다.


다른 사람들이 수없이 언급했듯이, 최소한의 놀라움의 원칙은 제어 흐름에만 지나치게 예외를 사용하는 것을 금지합니다. 반면에, 어떤 규칙이 100 % 정확하지 않으며, 예외가 "딱 맞는 도구가"이러한 경우는 항상있다 - 많은처럼 goto자체 방식에 의해의 형태로하는 선박 breakcontinue자바와 같은 언어로 된 항상 피할 수없는 중첩 루프에서 벗어나는 완벽한 방법입니다.

다음 블로그 게시물은 로컬아닌 사용자를 위해 다소 복잡하지만 흥미로운 사용 사례를 설명합니다 ControlFlowException.

JOOQ (Java 용 SQL 추상화 라이브러리)의 내부에서 일부 "희귀"조건이 충족 될 때 SQL 렌더링 프로세스를 일찍 중단하는 데 때때로 이러한 예외가 사용되는 경우를 설명합니다.

그러한 조건의 예는 다음과 같습니다.

  • 너무 많은 바인드 값이 있습니다. 일부 데이터베이스는 SQL 문에서 임의의 수의 바인드 값을 지원하지 않습니다 (SQLite : 999, Ingres 10.1.0 : 1024, Sybase ASE 15.5 : 2000, SQL Server 2008 : 2100). 이 경우 jOOQ는 SQL 렌더링 단계를 중단하고 인라인 된 바인드 값을 사용하여 SQL 문을 다시 렌더링합니다. 예:

    // Pseudo-code attaching a "handler" that will
    // abort query rendering once the maximum number
    // of bind values was exceeded:
    context.attachBindValueCounter();
    String sql;
    try {
    
      // In most cases, this will succeed:
      sql = query.render();
    }
    catch (ReRenderWithInlinedVariables e) {
      sql = query.renderWithInlinedBindValues();
    }
    

    쿼리 AST에서 바인딩 값을 추출하여 매번 계산할 경우이 문제가 발생하지 않는 쿼리의 99.9 %에 대해 가치있는 CPU 사이클을 낭비하게됩니다.

  • 일부 논리는 API를 통해 간접적으로 만 사용할 수 있습니다.이 API는 "부분적으로"실행하려고합니다. 이 UpdatableRecord.store()메서드는 내부 플래그 에 따라 INSERT또는 UPDATE문을 생성합니다 Record. "외부"에서 어떤 종류의 로직 store()(예 : 낙관적 잠금, 이벤트 리스너 처리 등) 이 포함되어 있는지 모르기 때문에 배치 문에 여러 레코드를 저장할 때 해당 논리를 반복하지 않으려 고합니다. 여기서 우리는 store()실제로 SQL 문을 생성하지 않고 SQL 문만을 생성하려고합니다. 예:

    // Pseudo-code attaching a "handler" that will
    // prevent query execution and throw exceptions
    // instead:
    context.attachQueryCollector();
    
    // Collect the SQL for every store operation
    for (int i = 0; i < records.length; i++) {
      try {
        records[i].store();
      }
    
      // The attached handler will result in this
      // exception being thrown rather than actually
      // storing records to the database
      catch (QueryCollectorException e) {
    
        // The exception is thrown after the rendered
        // SQL statement is available
        queries.add(e.query());                
      }
    }
    

    SQL store()을 선택적으로 실행 하지 않도록 사용자 정의 할 수있는 "재사용 가능한"API로 로직을 외부화 한 경우 , 유지 보수하기가 어려우며 재사용이 거의 불가능한 API 를 만드는 방법을 모색하고 있습니다.

결론

본질적으로 이러한 비 지역 goto의 사용법은 [Mason Wheeler] [5]가 대답 한 바에 따른 것입니다.

"나는이 시점에서 적절하게 처리 할 수없는 상황에 직면했다. 왜냐하면 나는 그것을 처리 할 수있는 충분한 컨텍스트가 없기 때문에 나를 호출 한 루틴 (또는 호출 스택의 상위 단계)은 처리 방법을 알아야한다. . "

두 가지 용도는 ControlFlowExceptions대안에 비해 구현하기가 쉽기 때문에 관련 내부에서 리팩터링하지 않고도 광범위한 로직을 재사용 할 수있었습니다.

그러나 미래의 유지 보수 담당자들에게 이것은 약간의 놀랄만 한 느낌이 남아 있습니다. 코드는 다소 섬세한 느낌이 들었고이 경우 올바른 선택 이었지만 일반 분기를 사용하는 것을 피하는 것이 쉬운 로컬 제어 흐름에 대한 예외를 사용하지 않는 것이 좋습니다 if - else.





language-agnostic