“하나의 귀국”이라는 개념은 어디에서 왔습니까?


1055

나는 종종 " 동일한 방법으로 여러 개의 리턴 문을 넣지 마십시오 "라고 말하는 프로그래머와 대화를 나눕니다 . 이유를 말해달라고 요청하면 " 코딩 표준이 그렇게 말합니다 "또는 " 혼란합니다 " 라고 말합니다 . 그들이 단일 return 문으로 솔루션을 보여 주면 코드가 나쁘게 보입니다. 예를 들면 다음과 같습니다.

if (condition)
   return 42;
else
   return 97;

" 이것은 추악합니다. 지역 변수를 사용해야합니다! "

int result;
if (condition)
   result = 42;
else
   result = 97;
return result;

이 50 % 코드 팽창은 프로그램을 이해하기 쉽게 만드는 방법은 무엇입니까? 개인적으로, 상태 공간이 쉽게 예방할 수있는 다른 변수에 의해 증가했기 때문에 더 어려워졌습니다.

물론 일반적으로 다음과 같이 작성합니다.

return (condition) ? 42 : 97;

그러나 많은 프로그래머가 조건부 연산자를 피하고 긴 형식을 선호합니다.

이 "한 번만 반환"이라는 개념은 어디에서 왔습니까? 이 협약이 시작된 역사적인 이유가 있습니까?


2
이것은 Guard Clause refactoring과 관련이 있습니다. stackoverflow.com/a/8493256/679340 Guard Clause는 메소드의 시작 부분에 수익을 추가합니다. 그리고 그것은 내 의견으로는 코드를 훨씬 깨끗하게 만듭니다.
Piotr Perak

3
그것은 구조적 프로그래밍이라는 개념에서 비롯된 것입니다. 일부는 단 하나의 리턴 만 있으면 리턴하기 전에 코드를 쉽게 수정하여 리턴하거나 쉽게 디버그 할 수 있다고 주장 할 수 있습니다.
martinkunev

3
나는 그 예가 어떤 식 으로든 강한 견해를 가지고 있지 않은 단순한 경우라고 생각합니다. 단일 항목-단일 출구 이상은 우리에게 15 가지 반환 진술 및 전혀 반환하지 않는 2 개의 더 많은 지점과 같은 미친 상황에서 멀어지게합니다!
mendota

2
그것은 내가 읽은 최악의 기사 중 하나입니다. 저자는 실제로 무언가를 달성하는 방법을 알아내는 것보다 OOP의 순도에 대해 환상을 그리는 데 더 많은 시간을 소비하는 것처럼 보입니다. 표현식 및 평가 트리에는 가치가 있지만 대신 일반 함수를 작성할 수는 없습니다.
DeadMG

3
조건을 모두 제거해야합니다. 답은 42입니다.
모호한

답변:


1119

"단일 입력, 단일 종료"는 대부분의 프로그래밍이 어셈블리 언어, FORTRAN 또는 COBOL로 수행 될 때 작성되었습니다. 현대 언어는 Dijkstra가 경고 한 관행을 지원하지 않기 때문에 널리 잘못 해석되었습니다.

"단일 항목"은 "기능에 대한 대체 진입 점을 만들지 않음"을 의미합니다. 물론 어셈블리 언어에서는 모든 명령에서 함수를 입력 할 수 있습니다. FORTRAN은 다음 ENTRY명령문 으로 함수에 여러 항목을 지원했습니다 .

      SUBROUTINE S(X, Y)
      R = SQRT(X*X + Y*Y)
C ALTERNATE ENTRY USED WHEN R IS ALREADY KNOWN
      ENTRY S2(R)
      ...
      RETURN
      END

C USAGE
      CALL S(3,4)
C ALTERNATE USAGE
      CALL S2(5)

"단일 종료"기능 만 반환해야한다는 의미 문 바로 호출 다음 한 곳. 그것은 않았다 하지 함수는 반환해야한다는 것을 의미 에서 한 곳. 구조적 프로그래밍 을 작성할 때 대체 위치로 리턴하여 함수가 오류를 표시하는 것이 일반적이었습니다. FORTRAN은 "alternate return"을 통해이를 지원했습니다.

C SUBROUTINE WITH ALTERNATE RETURN.  THE '*' IS A PLACE HOLDER FOR THE ERROR RETURN
      SUBROUTINE QSOLVE(A, B, C, X1, X2, *)
      DISCR = B*B - 4*A*C
C NO SOLUTIONS, RETURN TO ERROR HANDLING LOCATION
      IF DISCR .LT. 0 RETURN 1
      SD = SQRT(DISCR)
      DENOM = 2*A
      X1 = (-B + SD) / DENOM
      X2 = (-B - SD) / DENOM
      RETURN
      END

C USE OF ALTERNATE RETURN
      CALL QSOLVE(1, 0, 1, X1, X2, *99)
C SOLUTION FOUND
      ...
C QSOLVE RETURNS HERE IF NO SOLUTIONS
99    PRINT 'NO SOLUTIONS'

이 두 기술 모두 오류가 발생하기 쉽습니다. 대체 항목을 사용하면 일부 변수가 초기화되지 않은 경우가 종종 있습니다. 대체 리턴을 사용하면 브랜치 조건이 브랜치에 인접하지 않고 서브 루틴의 어딘가에 있다는 추가 복잡성과 함께 GOTO 문의 모든 문제점이있었습니다.


38
스파게티 코드를 잊지 마십시오 . 서브 루틴이 리턴 대신 GOTO를 사용하여 종료되는 것을 알 수 없었으며, 함수 호출 매개 변수 및 리턴 주소는 스택에 남았습니다. 단일 종료는 최소한 모든 코드 경로를 RETURN 문으로 퍼널 링하는 방법으로 승격되었습니다.
TMN

2
@TMN : 초기에는 대부분의 컴퓨터에 하드웨어 스택이 없었습니다. 재귀는 일반적으로 지원되지 않았습니다. 서브 루틴 인수 및 리턴 주소는 서브 루틴 코드에 인접한 고정 된 위치에 저장되었습니다. 귀환은 단지 간접적 인 고토였다.
케빈 클라인

5
@ kevin : 예, 그러나 당신에 따르면 이것이 더 이상 발명 된 것을 의미하지는 않습니다. (BTW, 프레드가 "Single Exit" 의 현재 해석에 대한 선호를 물었다는 사실을 합리적으로 확신합니다 .) 또한 C는 const여기에 많은 사용자가 태어나 기 전부터 더 이상 자본 상수가 필요하지 않았습니다. C에서도 그렇다. 그러나 자바는 나쁜 C 습관을 모두 보존했다 .
sbi

3
따라서 예외가 단일 종료에 대한이 해석을 위반합니까? (또는 그들의 더 원시적 인 사촌, setjmp/longjmp?)
Mason Wheeler

2
op가 단일 수익에 대한 현재 해석에 대해 물었음에도 불구하고,이 답변은 가장 역사적인 뿌리를 가진 답입니다. 언어가 .NET이 아닌 굉장한 VB와 일치하지 않으려면 단일 반환 값을 규칙 으로 사용하는 것이 중요하지 않습니다. 비 단락 부울 논리도 사용하는 것을 잊지 마십시오.
acelent

912

이 SESE ( Single Entry, Single Exit) 개념은 C 및 어셈블리와 같은 명시 적 리소스 관리 기능이있는 언어 에서 비롯됩니다 . C에서 다음과 같은 코드는 리소스를 누출시킵니다.

void f()
{
  resource res = acquire_resource();  // think malloc()
  if( f1(res) )
    return; // leaks res
  f2(res);
  release_resource(res);  // think free()
}

이러한 언어에서는 기본적으로 세 가지 옵션이 있습니다.

  • 정리 코드를 복제하십시오.
    어. 중복성은 항상 나쁩니다.

  • a goto를 사용 하여 정리 코드로 이동하십시오.
    이를 위해서는 정리 코드가 함수의 마지막 항목이어야합니다. (그리고 이것이 일부 사람들 goto이 그 자리를 차지 한다고 주장하는 이유 입니다. 그리고 실제로 – C에서)

  • 지역 변수를 소개하고이를 통해 제어 흐름을 조작하십시오.
    단점은 (생각 구문을 통해 조작하는 제어 흐름이다 break, return, if, while변수의 상태를 통해 조작 제어 흐름 (당신이 알고리즘에서 볼 때이 변수가 어떤 상태가 없기 때문에)보다 따라하기가 훨씬 쉽다).

어셈블리에서는 함수를 호출 할 때 함수의 주소로 이동할 수 있기 때문에 더 이상합니다. 효과적으로 거의 모든 함수에 대한 진입 점이 거의 없습니다. (때로는 이것이 도움이됩니다. 이러한 썽 크는 컴파일러가 C ++의 다중 상속 시나리오에서 함수 this를 호출하는 데 필요한 포인터 조정 을 구현하는 일반적인 기술입니다 virtual.)

리소스를 수동으로 관리해야하는 경우 어디에서나 함수를 시작하거나 종료하는 옵션을 활용하면 코드가 더 복잡해져 버그가 발생합니다. 따라서 더 깨끗한 코드와 버그를 줄이기 위해 SESE를 전파하는 사고 방식이 나타났습니다.


그러나 언어에 예외가있는 경우 (거의) 모든 기능이 (거의) 어느 시점에서 조기에 종료 될 수 있으므로 어쨌든 조기 반환을위한 준비를해야합니다. (내 생각은 finally주로 자바가 사용되며 using구현할 때 ( IDisposable, finally기타) C #에서, C + +를 대신 사용 RAII를 .)이 작업을 완료 한 후에는 할 수없는 때문에 초기에 자신 뒤처리에 실패 return성명, 어떤 일이 아마 SESE를지지하는 가장 강력한 주장은 사라졌다.

가독성이 남습니다. 물론, 십여 개의 return문이 무작위로 뿌려진 200 LoC 함수 는 좋은 프로그래밍 스타일이 아니며 읽을 수있는 코드를 만들지 않습니다. 그러나 그러한 함수는 조기 수익이 없으면 이해하기 쉽지 않습니다.

자원을 수동으로 관리하지 않거나 관리하지 않아야하는 언어에서는 이전 SESE 규칙을 준수 할 가치가 거의 없습니다. 위에서 언급 한 바와 같이, OTOH는 종종 코드를 더 복잡하게 만듭니다 . 공룡은 (C 제외) 오늘날 대부분의 언어에 잘 맞지 않습니다. 코드의 이해를 돕는 대신 방해합니다.


Java 프로그래머는 왜 이것을 고집합니까? 잘 모르겠지만 Java (외부) POV에서 Java는 C (많은 의미가있는 곳)에서 많은 규칙을 취하여 OO 세계 (그들이 쓸모 없거나 완전히 나쁜 곳)에 적용했습니다. 비용에 관계없이 (범위의 시작 부분에서 모든 변수를 정의하는 규칙처럼)

프로그래머는 비이성적 인 이유로 모든 종류의 이상한 표기법을 고수합니다. (깊숙히 중첩 된 구조적 설명 –“화살촉”–은 파스칼과 같은 언어에서 한때는 아름다운 코드로 여겨졌습니다.) 이것에 순수한 논리적 추론을 적용하는 것은 대다수의 사람들이 확립 된 방식에서 벗어나도록 설득하지 못하는 것 같습니다. 그러한 습관을 바꾸는 가장 좋은 방법은 아마도 기존 습관이 아닌 최선을 다하도록 일찍 가르치는 것입니다. 프로그래밍 교사 인 당신은 당신의 손에 있습니다.:)


52
권리. Java에서 정리 코드는 finally초기 return또는 예외에 관계없이 실행되는 절에 속합니다 .
dan04

15
Java 7의 @ dan04는 finally대부분의 시간 조차 필요하지 않습니다 .
R. Martinho Fernandes

93
@ 스티븐 : 물론 당신은 그것을 보여줄 수 있습니다! 실제로, 코드를 더 간단하고 이해하기 쉽게 표시 할 수있는 기능으로 복잡하고 복잡한 코드를 표시 할 수 있습니다. 모든 것이 남용 될 수 있습니다. 요점은 이해하기 쉽도록 코드를 작성하는 것이며 SESE를 창 밖으로 던질 때 다른 언어에 적용되는 오래된 습관을 망쳐 놓는 것입니다. 그러나 코드를 더 쉽게 읽을 수 있다고 생각한다면 변수로 실행을 제어하는 ​​것을 망설이지 않습니다. 거의 20 년 동안 그러한 코드를 본 것을 기억할 수 없다는 것입니다.
sbi

21
@Karl : 실제로 Java와 같은 GC 언어의 심각한 단점은 하나의 리소스를 정리하지 않아도되지만 나머지는 모두 실패합니다. (C ++를 사용하여 모든 자원에 대해이 문제를 해결 RAII를 .)하지만이 경우에도 전용 메모리의 이야기되지 않았다 (I 만 넣어 malloc()free()예를 들어 코멘트에), 나는 일반적으로 자원에 대해 얘기했다. 또한 GC가 이러한 문제를 해결할 것이라고 암시하지 않았습니다. (나는 상자에서 GC가없는 C ++에 대해 언급했다.) 내가 이해 한 바에 따르면 Java finally에서는이 문제를 해결하는 데 사용된다.
sbi

10
@sbi : 한 페이지를 넘지 않는 것보다 함수 (프로 시저, 방법 등)에서 더 중요한 것은 함수가 명확하게 정의 된 계약을 갖는 것입니다. 임의의 길이 제약 조건을 충족시키기 위해 잘게 잘려서 명확한 일을하지 않으면 좋지 않습니다. 프로그래밍은 서로 상충되는 서로 다른, 때로는 충돌하는 힘을 재생하는 것입니다.
Donal Fellows

81

한편으로, 단일 return 문은 로깅을보다 쉽게 ​​로깅 할 수있을뿐만 아니라 로깅에 의존하는 디버깅 형식을 제공합니다. 한 번에 반환 값을 인쇄하기 위해 함수를 단일 반환으로 줄여야하는 많은 시간을 기억합니다.

  int function() {
     if (bidi) { print("return 1"); return 1; }
     for (int i = 0; i < n; i++) {
       if (vidi) { print("return 2"); return 2;}
     }
     print("return 3");
     return 3;
  }

반면에,이를 function()해당 호출 로 리팩터링 _function()하고 결과를 기록 할 수 있습니다.


31
또한 함수에서 모든 엑시트 *를 포착하기 위해 하나의 중단 점 만 설정하면되기 때문에 디버깅이 더 쉬워 진다고 덧붙입니다. 일부 IDE를 사용하면 동일한 작업을 수행하기 위해 함수의 닫는 괄호에 중단 점을 둘 수 있습니다. (* exit로 전화하지 않는 한)
Skizz

3
비슷한 이유로, 매번 반환하기 전에 새로운 기능을 삽입 할 필요가 없으므로 기능을 확장 (추가)하는 것이 더 쉽습니다. 예를 들어 함수 호출 결과로 로그를 업데이트해야한다고 가정 해보십시오.
JeffSahol

63
솔직히, 해당 코드를 유지 관리하는 경우 적절한 장소에 s를 _function()사용 하여 현명하게 정의 하고 외부 로깅을 처리 return하는 명명 된 래퍼를 function()function()습니다. 왜냐하면 모든 반환을 단일 출구에 맞추기 위해 뒤틀린 논리 가있는 싱글보다 그 지점 앞에 추가 진술을 삽입 할 수 있도록하십시오.
ruakh

11
일부 디버거 (MSVS)에서는 마지막 닫는 중괄호에 중단 점을 둘 수 있습니다
Abyx

6
인쇄! = 디버깅. 그것은 전혀 논쟁이 아닙니다.
Piotr Perak

53

"Single Entry, Single Exit"는 Edsger W. Dijkstra의 편집자 " GOTO 성명서의 유해성 "에 대한 편지에 의해 시작된 1970 년대 초의 구조적 프로그래밍 혁명에서 시작되었습니다 . 구조화 된 프로그래밍의 개념은 Ole Johan-Dahl, Edsger W. Dijkstra 및 Charles Anthony Richard Hoare의 고전적인 책 "Structured Programming"에 자세히 설명되어 있습니다.

오늘도 "GOTO 성명서가 해로운 것으로 간주 됨"이 필요합니다. "Structured Programming"은 날짜가 있지만 여전히 매우 보람이 있으며 모든 개발자의 "Must Read"목록의 최상위에 있어야합니다 (예 : Steve McConnell). (Dahl 's 섹션은 Simula 67의 클래스 기본 사항을 설명합니다. Simula 67은 C ++ 클래스와 모든 객체 지향 프로그래밍의 기술 기반입니다.)


6
이 기사는 GOTO가 많이 사용 된 C 일 전에 작성되었습니다. 그들은 적이 아니지만이 대답은 확실히 맞습니다. 함수의 끝이 아닌 리턴 문은 사실상 goto입니다.
user606723

31
때 문서는 또한 일에 기록 된 goto그대로 갈 수 어디서나 바로 다른 함수에서 어떤 임의의 시점에 같은 프로 시저, 함수, 호출 스택 등 없음 제정신 언어 허가의 개념을 거치지 않고, 그 직선과 요즘 goto. C 's setjmp/ longjmp는 내가 알고있는 유일한 반 예외적 인 사례이며 심지어 양쪽 끝에서 협력이 필요합니다. (예외가 거의 똑같은 일을한다는 점을 고려할 때 "예외"라는 단어를 사용한 반 이론은 ...) 기본적으로이 기사는 오래 전에 죽은 관행을 권장하지 않습니다.
cHao

5
"[2]에서 Guiseppe Jacopini는 go to statement의 (논리적) 불필요성을 입증 한 것으로 보인다. 임의의 흐름도를 기계적으로 점프하는 것으로 다소간에 하나 이하는, 그러나, 권장되는 것은 아니다 . 그런 다음 , 생성 흐름도는 원래보다 투명 할 것으로 예상 할 수 없다. "
hugomg

10
이것이 질문과 어떤 관련이 있습니까? 예, Dijkstra의 연구는 결국 SESE 언어로 이어졌습니다. Babbage의 작업도 마찬가지였습니다. 당신이라고 생각한다면 아마 당신은 종이를 다시 읽어야 어떤 함수에서 여러 종료 지점을 가진 약을. 그렇지 않기 때문에.
jalf

10
@ 존, 당신은 실제로 대답하지 않고 질문에 대답하려고하는 것 같습니다. 그것은 훌륭한 독서 목록이지만, 당신은이 에세이와 서적이 주장하는 사람의 우려에 대해 할 말이 있다고 주장하는 것을 정당화하기 위해 어떤 것도 인용하거나 해석하지 않았습니다. 실제로, 당신의 의견 외에는 그 질문에 대해 실질적으로 아무 말도하지 않았습니다 . 이 답변을 확장 해보십시오.
Shog9

35

Fowler를 연결하는 것은 항상 쉽습니다.

SESE에 반대하는 주요 예 중 하나는 guard 절입니다.

중첩 조건부를 가드 절로 교체

모든 특별한 경우에 가드 조항 사용

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
return result;
};  

                                                                                                         http://www.refactoring.com/catalog/arrow.gif

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();
    return normalPayAmount();
};  

자세한 내용은 리팩토링 250 페이지를 참조하십시오 .


11
또 다른 나쁜 예 : else-if로 쉽게 수정할 수 있습니다.
Jack

1
귀하의 예는 공정하지 않습니다.이 방법은 다음과 같습니다. double getPayAmount () {double ret = normalPayAmount (); if (_isDead) ret = deadAmount (); 만약 (_isSeparated) ret = detachAmount (); 만약 (_isRetired) ret = retiredAmount (); 리트 윗을 반환; };
Charbel

6
@Charbel 그것은 같은 것이 아닙니다. 경우 _isSeparated_isRetired모두 사실 일 수 있습니다 (그리고 그 이유는 가능하지 않을까요?) 잘못된 금액을 반환합니다.
hvd

2
@Konchog " 중첩 조건문은 guard 절보다 더 나은 실행 시간을 제공합니다. "이것은 주로 인용 이 필요합니다. 나는 그것이 확실히 사실이라는 의심이 든다. 예를 들어,이 경우, 초기 코드는 생성 된 코드 측면에서 논리적 단락과 어떻게 다른가요? 그것이 중요하더라도, 그 차이가 무한한 은색 이상인 경우를 상상할 수 없습니다. 따라서 코드의 가독성을 높이기 위해 조기 최적화를 적용하는 것입니다. 생각보다 약간 더 빠른 코드로 이끄는 이론적 포인트를 만족시키기 위해서입니다. 우리는 여기서 그렇게하지 않습니다
underscore_d

1
@underscore_d, 맞습니다. 컴파일러에 많이 의존하지만 더 많은 공간을 차지할 수 있습니다. 두 개의 의사 어셈블리를 살펴보면 왜 가드 조항이 고급 언어에서 나오는지 쉽게 알 수 있습니다. "A"테스트 (1); branch_fail 끝; 시험 (2); branch_fail 끝; 시험 (3); branch_fail 끝; {CODE} 끝 : return; "B"테스트 (1); branch_good next1; 반환; next1 : 테스트 (2); branch_good next2; 반환; next2 : 테스트 (3); branch_good next3; 반환; next3 : {CODE} 리턴;
Konchog

11

나는이 주제에 대해 얼마 전에 블로그 게시물을 썼습니다.

결론은이 규칙은 가비지 수집 또는 예외 처리가없는 언어의 시대에서 나온 것입니다. 이 규칙이 현대 언어로 더 나은 코드를 생성한다는 것을 보여주는 공식적인 연구는 없습니다. 더 짧거나 더 읽기 쉬운 코드로 이어질 때마다 무시하십시오. 이것을 주장하는 Java 사람들은 구식이며 무의미한 규칙에 따라 맹목적이고 의문의 여지가 없습니다.

이 질문은 또한 질문했습니다


이봐, 난 더 이상 그 링크에 도달 할 수 없습니다. 어딘가에서 호스팅되는 버전에 여전히 액세스 할 수 있습니까?
Nic Hartley

안녕하세요, QPT, 좋은 자리입니다. 블로그 게시물을 다시 가져와 위의 URL을 업데이트했습니다. 지금 연결해야합니다!
Anthony

그것보다 더 많은 것이 있습니다. SESE를 사용하면 정확한 실행 타이밍을 훨씬 쉽게 관리 할 수 ​​있습니다. 어쨌든 스위치를 사용하여 중첩 된 조건을 리팩토링 할 수 있습니다. 반환 값이 있는지 여부에 관한 것이 아닙니다.
Konchog

당신이 그것을지지하는 공식적인 연구가 없다고 주장한다면, 그것에 반대하는 연구에 연결하는 것이 당신에게 좋지 않을 것입니다.
Mehrdad

Mehrdad, 그것을지지하는 공식적인 연구가 있다면 그것을 보여주십시오. 그게 다야. 증거에 대한 주장은 증거의 부담을 이동시키는 것입니다.
Anthony

7

한 번의 반환으로 리팩토링이 더 쉬워집니다. 리턴, 중단 또는 계속을 포함하는 for 루프의 내부에 "추출 방법"을 수행하십시오. 제어 흐름이 끊어지면 실패합니다.

요점은 : 아무도 완벽한 코드를 작성하는 척하는 사람이 없다고 생각합니다. 따라서 코드는 꾸준히 리팩토링되어 "개선"되고 확장됩니다. 그래서 내 목표는 코드를 리팩토링 가능한 한 친절하게 유지하는 것입니다.

제어 흐름 차단기가 포함되어 있고 기능을 거의 추가하지 않으려는 경우 함수를 완전히 재구성해야하는 문제에 직면하는 경우가 많습니다. 격리 된 중첩에 새 경로를 도입하는 대신 전체 제어 흐름을 변경하면 오류가 발생하기 쉽습니다. 끝에 단 하나의 리턴 만 있거나 가드를 사용하여 루프를 종료하면 더 많은 중첩과 코드가 있습니다. 그러나 컴파일러 및 IDE 지원 리팩토링 기능을 얻을 수 있습니다.


변수에도 동일하게 적용됩니다. 조기 반환과 같은 제어 흐름 구조를 사용하는 대신 사용할 수있는 방법은 다음과 같습니다.
중복 제거기

변수는 대부분 기존 제어 흐름이 유지되는 방식으로 코드를 조각으로 나누는 것을 방해하지 않습니다. "추출 방법"을 시도하십시오. IDE는 사용자가 작성한 내용에서 의미를 도출 할 수 없으므로 제어 흐름 사전 생성 리팩토링 만 수행 할 수 있습니다.
oopexpert

5

여러 개의 return 문이 GOTO를 단일 return 문에 갖는 것과 같다는 사실을 고려하십시오. break 문도 마찬가지입니다. 따라서 나와 같은 일부 사람들은 모든 의도와 목적을 위해 GOTO를 고려합니다.

그러나 이러한 유형의 GOTO는 유해한 것으로 간주하지 않으며 적절한 이유를 찾은 경우 주 코드에서 실제 GOTO를 사용하는 것을 망설이지 않습니다.

나의 일반적인 규칙은 GOTO는 흐름 제어만을위한 것입니다. 루핑에는 절대 사용해서는 안되며 '위로'또는 '뒤로'로 절대 GOTO해서는 안됩니다. (이것은 휴식 / 반품이 작동하는 방법입니다)

다른 사람들이 언급했듯이, 다음은 유해한 것으로 간주되는 GOTO 선언문을 반드시 읽어야합니다.
그러나 이것은 GOTO가 과도하게 사용되었을 때 1970 년에 작성된 것임을 명심하십시오. 모든 GOTO가 해로운 것은 아니며 정상적인 구문 대신 사용하지 않는 한 정상적인 구문을 사용하는 것이 매우 불편한 이상한 경우에는 사용을 권장하지 않습니다.

나는 때로는 유용한 경우가 일반적으로 발생하지 않는 고장으로 인해 영역을 탈출 해야하는 오류 사례에서 그것들을 사용한다는 것을 알았습니다. 그러나 GOTO를 사용하는 대신 일찍 반환 할 수 있도록이 코드를 별도의 함수에 넣는 것도 고려해야하지만 때로는 불편합니다.


6
gotos를 대체하는 모든 구조화 된 구성은 goto의 관점에서 구현됩니다. 예를 들어 "if"및 "case"루프입니다. 이것은 그들을 나쁘게 만들지 않습니다-사실 그 반대입니다. 또한 "의도 및 목적"입니다.
Anthony

Touche, 그러나 이것은 내 요점과 다르지 않습니다 ... 단지 내 설명이 약간 잘못됩니다. 오 잘
user606723

GOTO는 (1) 대상이 동일한 방법 또는 기능 내에 있고 (2) 방향이 코드에서 앞으로 나오고 (일부 코드 건너 뛰기) (3) 대상이 다른 중첩 구조 안에 있지 않는 한 항상 양호해야합니다 (예 : if-case 중간에서 else-case 중간까지의 GOTO). 이 규칙을 따르면 GOTO의 모든 오용은 시각적으로나 논리적으로 냄새가 심합니다.
Mikko Rantalainen

3

순환 복잡성

SonarCube가 순환 복잡성을 결정하기 위해 여러 반환 문을 사용하는 것을 보았습니다. 리턴 문이 많을수록 순환 복잡성이 높아집니다.

반품 유형 변경

다중 리턴은 리턴 유형을 변경하기로 결정할 때 함수의 여러 위치에서 변경해야 함을 의미합니다.

다중 이탈

반환 된 값의 원인을 이해하려면 조건문과 함께 논리를 신중하게 조사해야하므로 디버깅하기가 더 어렵습니다.

리팩토링 솔루션

여러 return 문에 대한 해결책은 필요한 구현 개체를 확인한 후 polymorphism으로 대체하여 단일 반환 값을 갖는 것입니다.


3
여러 장소에서 여러 장소로 반품 가치를 설정하는 것으로 이동해도 순환 복잡성이 제거되지 않고 출구 위치 만 통합됩니다. 주어진 맥락에서 순환 복잡성이 나타낼 수있는 모든 문제는 남아 있습니다. "반환 된 값의 원인을 이해하기 위해 조건문과 함께 논리를 신중하게 연구해야하기 때문에 디버그하기가 더 어렵습니다."다시 말하지만, 논리는 리턴을 통합하여 변경되지 않습니다. 작동 방식을 이해하기 위해 코드를주의 깊게 연구해야하는 경우 리팩터링해야합니다.
WillD
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.