정상적인 경로를 따라야합니까, 아니면 일찍 실패해야합니까?


73

로부터 코드 전체 책 다음 인용 온다 :

"일반적인 경우 ifelse"

즉, 표준 경로에서 예외 / 편차가 발생해야합니다 else.

그러나 Pragmatic Programmer 는 "초기 충돌"(p. 120)을 지시합니다.

어떤 규칙을 따라야합니까?


15
@gnat 복제되지 않음
BЈовић

16
둘은 상호 배타적이지 않으며 모호성이 없습니다.
jwenting

6
코드 완성 인용문이 그렇게 훌륭한 조언인지 확실하지 않습니다. 아마도 가독성을 높이려는 시도 일 것입니다. 그러나 드문 경우가 텍스트로 먼저 나오는 상황이 있습니다. 이것은 대답이 아닙니다. 기존 답변은 이미 귀하의 질문이 잘 나온 혼란을 설명합니다.
Eamon Nerbonne

14
일찍 충돌하고 열심히 충돌하십시오! if지점 중 하나가 반환되면 먼저 사용하십시오. else나머지 코드는 피하십시오 . 사전 조건이 실패한 경우 이미 반환했습니다. 코드는 읽기 쉽고 들여 쓰기가 적습니다 ...
CodeAngry

5
이것이 가독성과 관련이 없다고 생각하는 사람은 나뿐 아니라 정적 분기 예측을 최적화하려는 잘못된 시도 일 것입니다.
Mehrdad

답변:


189

"Crash early"은 어떤 코드 줄이 텍스트보다 먼저 나오는지에 관한 것이 아닙니다. 처리 가능한 초기 단계에서 오류를 감지하여 실수로 이미 결함이있는 상태를 기반으로 의사 결정 및 계산을하지 않도록 지시합니다 .

에서 if/ else둘 다에 "이전"또는 "이상"공정을 구성하는 말했다 수 있도록 구조의 블록 중 하나만이 실행된다. 따라서 주문 방법은 가독성의 문제이며, "실패"는 결정에 들어 가지 않습니다.


2
일반적인 요점은 훌륭하지만 한 가지 문제가 있습니다. 있는 경우 (if / else if / else) "if if"문에서 표현식 다음에 "else if"로 평가 된 표현식이 평가됩니다. 중요한 점은, 가독성과 개념적 처리 단위입니다. 하나의 코드 블록 만 실행되지만 여러 조건을 평가할 수 있습니다.
Encaitar

7
@Encaitar, 그 세분성 수준은 "초기 실패"라는 문구가 사용될 때 일반적으로 의도 된 것보다 훨씬 작습니다.
riwalk

2
@Encaitar 그것은 프로그래밍의 기술입니다. 여러 번의 점검에 직면 할 때 가장 먼저 생각할 수있는 방법을 시도하는 것이 좋습니다. 그러나이 정보는 디자인 타임에는 알 수 없지만 최적화 단계에서는 알 수 있지만 조기 최적화에는주의하십시오.
BPugh

공정한 의견. 이것은 좋은 답변이었고 나중에 참조하기 위해 더 나은 것을 만들기 위해 노력하고 싶었습니다.
Encaitar

JavaScript, Python, perl, PHP, bash 등과 같은 스크립팅 언어는 선형 적으로 해석되므로 예외입니다. 작은 if/else구조에서는 아마 중요하지 않습니다. 그러나 루프에서 호출되거나 각 블록에 많은 명령문이있는 것은 가장 일반적인 조건으로 먼저 더 빨리 실행될 수 있습니다.
DocSalvager

116

귀하의 경우 else문 만 실패 코드를 포함, 대부분의 경우는 없을 것이다.

이것을하는 대신 :

if file.exists() :
  if validate(file) :
    # do stuff with file...
  else :
    throw foodAtMummy
else :
  throw toysOutOfPram

이 작업을 수행

if not file.exists() :
  throw toysOutOfPram

if not validate(file) :
  throw foodAtMummy

# do stuff with file...

단순히 오류 검사를 포함하기 위해 코드를 깊이 중첩하고 싶지는 않습니다.

그리고 다른 사람들이 이미 언급했듯이 두 가지 조언은 모순되지 않습니다. 하나는 실행 순서 에 관한 것이고 다른 하나는 코드 순서 에 관한 것 입니다.


4
! 가 없으면 if블록 에 정상적인 흐름을 유지하고 블록에 예외적 인 흐름을 else적용 하라는 조언이 적용되지 않습니다 else. 이와 같은 보호문은 대부분의 코딩 스타일에서 오류 조건을 처리하기 위해 선호되는 형식입니다.
Jules

+1 이것은 좋은 지적이며 실제로 오류 조건을 가진 물건을 주문하는 방법에 대한 실제 질문에 대한 답을 제공합니다.
ashes999

확실히 더 명확하고 유지 관리하기 쉽습니다. 이것이 내가 좋아하는 방법입니다.
John

27

둘 다 따라야합니다.

"Crash early"/ fail early advice는 가능한 빨리 오류가 있는지 입력을 테스트해야한다는 것을 의미합니다.
예를 들어, 분석법이 양수 (> 0)로 간주되는 크기 나 개수를 받아 들인 경우 조기 조언 실패는 알고리즘이 말이되지 않는 것을 기다리지 않고 분석법의 시작 부분에서 해당 조건을 테스트 함을 의미합니다. 결과.

정상적인 경우를 우선으로하는 조언은 조건을 테스트 할 경우 가장 가능성이 높은 경로가 우선임을 의미합니다. 이는 프로세서의 분기 예측이 더 낫기 때문에 성능과 가독성에 도움이됩니다. 일반적인 경우에 함수가 수행하는 작업을 알아낼 때 코드 블록을 건너 뛸 필요가 없기 때문입니다.
이 조언은 if (!precondition) throw코드를 읽는 동안 건너 뛸 오류 처리가 없기 때문에 전제 조건을 테스트하고 즉시 어설 션 또는 구문 을 사용하여 구제 할 때 적용되지 않습니다 .


1
분기 예측 부분을 자세히 설명 할 수 있습니까? if 케이스로 갈 가능성이 높은 코드는 else 케이스로 갈 가능성이 높은 코드보다 더 빠를 것으로 예상하지 않습니다. 그것은 지점 예측의 요점입니다. 그렇지 않습니까?
Roman Reiner 2018 년

@ user136712 : 최신 (고속) 프로세서에서는 이전 명령이 처리를 완료하기 전에 명령을 가져옵니다. 분기 예측은 조건 분기를 실행하는 동안 페치 된 명령이 실행하기에 적합한 명령 일 가능성을 높이기 위해 사용됩니다.
Bart van Ingen Schenau

2
분기 예측이 무엇인지 알고 있습니다. 내가 게시물을 올바르게 읽으면 지점 예측 if(cond){/*more likely code*/}else{/*less likely code*/}보다 빨리 실행됩니다 if(!cond){/*less likely code*/}else{/*more likely code*/}. 지점 예측이 문 if또는 else문에 편향되지 않고 역사 만 고려 한다고 생각 합니다. 따라서 else발생 가능성이 더 높으면이를 정확하게 예측할 수 있어야합니다. 이 가정이 거짓입니까?
Roman Reiner

18

@JackAidley가 이미 그 요점을 말했다고 생각 하지만 다음과 같이 공식화하겠습니다.

예외없이 (예 : C)

규칙적인 코드 흐름에는 다음이 있습니다.

if (condition) {
    statement;
} else if (less_likely_condition) {
    less_likely_statement;
} else {
    least_likely_statement;
}
more_statements;

"초기 오류"사례에서 코드는 갑자기 다음과 같이 읽습니다.

/* demonstration example, do NOT code like this */
if (condition) {
    statement;
} else {
    error_handling;
    return;
}

이 패턴을 발견하는 경우 - a는 returnelse(또는 if문제의 코드는 않도록) 블록, 즉시 재 작업 하지else블록 :

/* only code like this at University, to please structured programming professors */
function foo {
    if (condition) {
        lots_of_statements;
    }
    return;
}

현실 세계에서…

/* code like this instead */
if (!condition) {
    error_handling;
    return;
}
lots_of_statements;

이것은 너무 깊이 중첩 방지 은 "초기 탈옥"경우 (마음을 유지하는 데 도움이 -하고 코드 흐름 - 청소)을 충족 하고 (가) "는에 가능성이 일을 넣어 위반하지 않는 if단순히 어떤이 없기 때문에 일부" else부분 .

C 정리

비슷한 질문에 대한 답변에서 영감을 얻었습니다 (이 잘못됨). C로 정리하는 방법은 다음과 같습니다. 하나 또는 두 개의 출구 지점을 사용할 수 있습니다. 여기에는 두 개의 출구 지점이 있습니다.

struct foo *
alloc_and_init(size_t arg1, int arg2)
{
    struct foo *res;

    if (!(res = calloc(sizeof(struct foo), 1)))
        return (NULL);

    if (foo_init1(res, arg1))
        goto err;
    res.arg1_inited = true;
    if (foo_init2(&(res->blah), arg2))
        goto err;
    foo_init_complete(res);
    return (res);

 err:
    /* safe because we use calloc and false == 0 */
    if (res.arg1_inited)
        foo_dispose1(res);
    free(res);
    return (NULL);
}

정리가 덜 필요한 경우 하나의 종료점으로 축소 할 수 있습니다.

char *
NULL_safe_strdup(const char *arg)
{
    char *res = NULL;

    if (arg == NULL)
        goto out;

    /* imagine more lines here */
    res = strdup(arg);

 out:
    return (res);
}

goto당신이 그것을 다룰 수 있다면, 이 사용 은 완벽하게 좋습니다. 사용 goto하지 않는 것이 좋은지, 수용 가능한지, 나쁜지, 스파게티 코드 등을 스스로 결정할 수없는 사람들을 대상으로합니다.

예외

위의 예외는 예외없이 언어에 대해 이야기하며, 나는 나 자신을 선호합니다 (명시 적 오류 처리를 훨씬 잘하고 훨씬 덜 놀라게 사용할 수 있습니다). igli를 인용하려면 :

<igli> exceptions: a truly awful implementation of quite a nice idea.
<igli> just about the worst way you could do something like that, afaic.
<igli> it's like anti-design.
<mirabilos> that too… may I quote you on that?
<igli> sure, tho i doubt anyone will listen ;)

그러나 예외가있는 언어로 잘 사용하는 방법과 잘 사용하고 싶을 때의 제안은 다음과 같습니다.

예외 상황에서 오류 반환

초기 return의 대부분을 예외를 던지는 것으로 바꿀 수 있습니다 . 그러나 , 귀하의 일반적인 코드의 흐름 즉, 프로그램의 흐름이있는 프로그램은 물론, 예외 ... 오류 상태가 발생하거나 somesuch하지 않습니다 예외를 올립니다.

이것은…

# this page is only available to logged-in users
if not isLoggedIn():
    # this is Python 2.5 style; insert your favourite raise/throw here
    raise "eh?"

… 괜찮지 만…

/* do not code like this! */
try {
    openFile(xyz, "rw");
} catch (LockedException e) {
    return "file is locked";
}
closeFile(xyz);
return "file is not locked";

… 아니다. 기본적으로 예외는 제어 흐름 요소가 아닙니다 . 또한, "자바 프로그래머는 항상 이러한 예외가 정상이라고 알려주는"조작이 이상하게 보이고 디버깅을 방해 할 수 있습니다 (예 : IDE가 예외를 중단하도록 지시). 예외는 종종 스택 백을 해제하기 위해 스택을 풀기 위해 런타임 환경을 요구합니다. 이에 대한 더 많은 이유가있을 수 있습니다.

예외를 지원하는 언어에서는 기존 논리 및 스타일과 일치하고 자연스럽게 느껴지는 것을 사용하십시오. 처음부터 무언가를 작성하는 경우 조기에 동의하십시오. 라이브러리를 처음부터 작성하는 경우 소비자를 생각하십시오. ( abort()도서관 에서도 사용하지 마십시오 .) 그러나 무엇을하든 일반적으로 그 이후에 작업이 정상적으로 계속되는 경우 예외가 발생하지 않습니다.

일반적인 조언 wrt. 예외

전체 개발자 팀이 먼저 합의한 예외를 프로그램 내에서 모두 사용하도록하십시오. 기본적으로 계획하십시오. 그것들을 풍부하게 사용하지 마십시오. 때로는 C ++, Java ™, Python에서도 오류 리턴이 더 좋습니다. 때로는 그렇지 않습니다. 생각과 함께 사용하십시오.


일반적으로 초기 수익은 코드 냄새로 간주됩니다. 전제 조건이 충족되지 않아 다음 코드가 실패하면 대신 예외가 발생합니다. 그냥 '선생님
DanMan

1
@ DanMan : 내 기사는 C로 작성되었습니다 ... 나는 보통 예외를 사용하지 않습니다. 그러나 나는 기사를 (아주 조금 길어졌다) 제안서로 확장했다. 예외; 우연히, 우리는 어제 회사의 내부 개발자 메일 링리스트에서 똑같은 질문을했습니다.
mirabilos

또한 한 줄의 if와 for에도 중괄호를 사용하십시오. 당신은 goto fail;식별에 숨겨진 또 다른 것을 원하지 않습니다 .
Bruno Kim

1
@BrunoKim : 이것은 작업하는 프로젝트의 스타일 / 코딩 규칙에 전적으로 달려 있습니다. 나는 이것이 찡그린 BSD와 함께 일한다 (더 많은 광학적 혼란과 수직 공간의 손실). 그러나 $ dayjob에서 우리가 동의 한대로 배치합니다 (신규 초보자에게는 어렵고 오류가 발생할 가능성이 적습니다).
mirabilos

3

제 생각에는 '가드 조건'은 코드를 읽을 수있는 가장 쉽고 쉬운 방법 중 하나입니다. 나는 if메소드의 시작 부분에서 볼 때 정말 싫어 하고 else코드가 화면에 없기 때문에 보이지 않습니다 . 그냥 보려면 아래로 스크롤해야합니다 throw new Exception.

코드를 읽는 사람이 코드를 읽는 방법 전체를 뛰어 넘을 필요는 없지만 항상 위쪽에서 아래쪽으로 스캔하도록 검사를 시작하십시오.


2

(@mirabilos의 답변 은 훌륭하지만 다음은 동일한 결론에 도달하기 위해 질문에 대해 어떻게 생각하는지입니다.)

나는 나중에 내 기능 코드를 읽는 나 자신 (또는 다른 사람)에 대해 생각하고 있습니다. 첫 번째 줄을 읽으면 입력에 대해 어떤 가정도 할 수 없습니다 (어쨌든 확인하지 않는 것을 제외하고). 그래서 제 생각은 "좋아, 내가 논증으로 일을 할 것이라는 것을 알고 있지만, 먼저 정리해 보자"는 말이다. , 나는 정상적인 경우를 조절 된 것으로 보지 않는다; 나는 그것이 정상임을 강조하고 싶다.

int foo(int* bar, int baz) {

   if (bar == NULL) /* I don't like you, leave me alone */;
   if (baz < 0) /* go away */;

   /* there, now I can do the work I came into this function to do,
      and I can safely forget about those if's above and make all 
      the assumptions I like. */

   /* etc. */
}

-3

이러한 종류의 조건 순서는 해당 코드 섹션의 중요도 및 사용할 수있는 기본값이 있는지 여부에 따라 다릅니다.

다시 말해:

A. 중요 섹션 및 기본값 없음 => 조기 실패

B. 중요하지 않은 섹션 및 기본값 => 다른 부분 에서 기본값 사용

C. 사례 간 => 필요에 따라 사례별로 결정


이것은 단지 당신의 의견입니까, 아니면 어떻게 든 설명 / 백업 할 수 있습니까?
gnat

각 옵션이 (실제로 많은 단어없이) 왜 사용되는지 설명 할 때 이것이 정확히 백업되지 않는 이유는 무엇입니까?
Nikos M.

나는 이것을 말하고 싶지 않지만 (my)이 답은 문맥에 맞지 않습니다 :). 이것은 OP가 묻는 질문인데, 당신이 대안적인 답변을 가지고 있는지는 또 다른 문제인지
Nikos M.

솔직히 여기서 설명을 보지 못했습니다. 예를 들어, "중요한 섹션이없고 기본값 => 일찍 실패하지 않음" 과 같은 다른 의견을 작성 하는 경우이 답변이 독자가 두 가지 반대 의견을 선택하는 데 어떻게 도움이됩니까? 고려 편집 에 맞게, 더 나은 모양으로 그것을 보내고 답변하는 방법 가이드 라인을.
gnat

알다시피, 이것은 실제로 다른 설명이 될 수 있지만 적어도 당신은 "중요한 부분"과 "기본값 없음"이라는 단어를 이해하는데, 이는 초기실패 하는 전략을 암시 할 수 있으며 이것은 최소한의 확장이지만 실제로는 확장입니다
Nikos M.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.