제어 흐름으로 인해 중복되는 상황에서 "else"를 사용해야합니까?


18

때로는 다음 예제와 유사한 코드를 우연히 발견했습니다 (이 함수의 기능은이 질문의 범위를 벗어납니다).

function doSomething(value) {
  if (check1(value)) {
    return -1;
  }
  else if (check2(value)) {
    return value;
  }
  else {
    return false;
  }
}

보다시피 if,, else ifelse명령문은 명령문과 함께 사용됩니다 return. 이것은 평범한 관찰자에게는 상당히 직관적 인 것처럼 보이지만 소프트웨어 개발자의 관점에서 else-s 를 삭제하고 다음 과 같이 코드를 단순화하는 것이 더 우아하다고 생각합니다 .

function doSomething(value) {
  if (check1(value)) {
    return -1;
  }
  if (check2(value)) {
    return value;
  }
  return false;
}

이는 return동일한 범위 내에서 명령문 다음에 오는 모든 것이 절대 실행되지 않으므로 위의 코드가 첫 번째 예제와 의미 적으로 동일하므로 의미가 있습니다.

위 중 어느 것이 좋은 코딩 방법에 더 적합합니까? 코드 가독성과 관련하여 두 방법 중 어느 하나의 단점이 있습니까?

편집 : 이 질문에 대한 중복 제안이 참조로 제공되었습니다. 나는 다른 질문에 제시된 것처럼 중복 진술을 피하는 것에 대해 묻지 않기 때문에 내 질문이 다른 주제를 다루고 있다고 생각합니다. 두 가지 질문 모두 약간 다른 방식으로 반복을 줄이려고합니다.


14
(가) else낮은 문제, 더 큰 문제는 분명히 함수의 API가 예측할 수 있도록하는 하나의 기능 중 2 개 데이터 형식을 반환하는 것입니다.
Andy

3
E_CLEARLY_NOTADUPE
kojiro

3
@DavidPacker : 적어도 두 개; 셋이 될 수 있습니다. -1숫자이고 false부울이며 value여기에 지정되어 있지 않으므로 모든 유형의 개체가 될 수 있습니다.
Yay295

1
@ Yay295 나는 value실제로 정수 라고 바랐다 . 그것이 무엇이든 될 수 있다면 훨씬 더 나빠집니다.
Andy

@DavidPacker 이것은 특히 좋은 관행에 관한 질문과 관련하여 제기 될 때 매우 중요한 포인트입니다. 본인과 Yay295에 동의하지만 코드 샘플은 관련없는 코딩 기술을 보여주는 예입니다. 그럼에도 불구하고, 이것은 중요한 문제이므로 실제 코드에서 간과해서는 안됩니다.
rhino

답변:


23

나는없는 사람을 좋아하며 else이유는 다음과 같습니다.

function doSomething(value) {
  //if (check1(value)) {
  //  return -1;
  //}
  if (check2(value)) {
    return value;
  }
  return false;
}

그렇게하는 것이 아무 것도 깨지지 않았기 때문입니다.

함수의 이름을 포함하여 모든 형태의 상호 의존성을 싫어하십시오 check2(). 분리 할 수있는 모든 것을 분리하십시오. 때때로 당신은 필요 else하지만 여기서는 보이지 않습니다.


나는 일반적으로 여기에 언급 된 이유로 이것을 선호합니다. 그러나 일반적으로 코드를 읽을 때 코드 블록 이후의 유일한 실행 이 명령문 의 조건 이 거짓 인 경우를 명확히하기 위해 각 if블록의 } //else끝에 주석을 배치합니다 . 이와 같은 것이 없으면, 특히 블록 내의 코드 가 더 복잡해 지면, 조건이 참일 때 블록을 넘어서 실행하지 않으려는 코드를 유지 관리하는 사람에게는 분명하지 않을 수 있습니다 . ififififif
Makyen

@Makyen 당신은 좋은 지적을 올립니다. 하나는 무시하려고했습니다. 또한 if이 코드 섹션이 완료되고 구조가 완료되었음을 분명히하기 위해 무언가를 수행해야한다고 생각합니다 . 이 작업을 수행하기 위해 여기서 수행하지 않은 작업을 수행합니다 (다른 변경을 수행하여 OP의 요점을 산만하게하고 싶지 않기 때문에). 산만하지 않고 모든 것을 달성 할 수있는 가장 간단한 일을합니다. 빈 줄을 추가합니다.
candied_orange

27

코드의 의미에 달려 있다고 생각합니다. 세 가지 사례가 서로 의존하는 경우 명시 적으로 명시하십시오. 이렇게하면 코드의 가독성이 향상되고 다른 사람이 더 쉽게 이해할 수 있습니다. 예:

if (x < 0) {
    return false;
} else if (x > 0) {
    return true;
} else {
    throw new IllegalArgumentException();
}

여기 x에서는 세 경우 모두 의 가치에 따라 명확하게 결정 됩니다. last를 생략하면 else덜 명확합니다.

사례가 서로 직접 의존하지 않는 경우 생략하십시오.

if (x < 0) {
    return false;
}
if (y < 0) {
    return true;
}
return false;

문맥없이 간단한 예에서 이것을 보여주기는 어렵지만 내 요점을 알기를 바랍니다.


6

나는 두 번째 옵션 (분리하지 선호하는 ifs더와 else if조기를 return) 하지만한 코드 블록이 짧은만큼 .

코드 블록이 길면을 사용하는 것이 좋습니다. 그렇지 않으면 코드 else if의 여러 종료점을 명확하게 이해하지 못하기 때문입니다.

예를 들면 다음과 같습니다.

function doSomething(value) {
  if (check1(value)) {
    // ...
    // imagine 200 lines of code
    // ...
    return -1;
  }
  if (check2(value)) {
    // ...
    // imagine 150 lines of code
    // ...
    return value;
  }
  return false;
}

이 경우 더 나은 사용이 가능 else if하고 리턴 코드를 변수에 저장하고 끝에 하나의 리턴 문장이 있습니다.

function doSomething(value) {
  retVal=0;
  if (check1(value)) {
    // ...
    // imagine 200 lines of code
    // ...
    retVal=-1;
  } else if (check2(value)) {
    // ...
    // imagne 150 lines of code
    retVal=value;
  }
  return retVal;
}

그러나 함수가 짧아지기 위해 노력해야하기 때문에 첫 번째 코드 스 니펫과 같이 짧게 유지하고 일찍 종료한다고 말하고 싶습니다.


1
나는 당신의 전제에 동의하지만, 우리가 코드를 작성하는 방법을 논의하는 한, 코드 블록이 짧아야한다는 것에 동의 할 수 있을까요? 선택 해야하는 경우 다른 코드를 사용하는 것보다 긴 코드 블록을 함수로 나누지 않는 것이 더 나쁘다고 생각합니다.
kojiro

1
흥미로운 주장. return코드가 3 줄 안에 if있기 때문에 else if200 줄의 코드 이후 와 다르지 않습니다 . 여기서 소개 하는 단일 리턴 스타일 은 예외와 함께 오류를보고하지 않는 c 및 기타 언어의 전통입니다. 요점은 리소스를 정리할 단일 장소를 만드는 것이 었습니다. 예외 언어는이를 위해 finally블록을 사용합니다. 디버거가 중괄호를 닫는 함수에 하나를 놓을 수 없다면 중단 점을 놓을 장소를 만든다고 생각합니다.
candied_orange

4
과제가 마음에 들지 않습니다 retVal. 함수를 안전하게 종료 할 수 있으면 함수에서 단일 종료 변수에 리턴되는 안전 값을 지정하지 않고 즉시 종료하십시오. 정말 긴 함수에서 단일 리턴 포인트를 사용하면 일반적으로 다른 프로그래머를 신뢰하지 않으며 리턴 값의 다른 수정을 위해 나머지 함수를 검토하게됩니다. 빨리 실패하고 일찍 돌아 오는 것은 내가 사는 두 가지 규칙입니다.
Andy

2
제 생각에는 긴 블록의 경우 훨씬 더 나쁩니다. 외부 컨텍스트가 무엇이든 가능한 빨리 함수를 종료하려고합니다.
Andy

2
나는 DavidPacker와 함께 있습니다. 단일 반환 스타일을 사용하면 할당 지점 뒤의 코드뿐만 아니라 모든 할당 지점을 연구하게됩니다. 초기 리턴 스타일의 종료점을 연구하는 것이 길거나 짧은 나에게 더 좋을 것입니다. 언어가 finally 블록을 허용하는 한.
candied_orange

4

나는 다른 상황에서 둘 다 사용합니다. 유효성 검사에서 else를 생략합니다. 제어 흐름에서는 else를 사용합니다.

bool doWork(string name) {
  if(name.empty()) {
    return false;
  }

  //...do work
  return true;
}

vs

bool doWork(int value) {
  if(value % 2 == 0) {
    doWorkWithEvenNumber(value);
  }
  else {
    doWorkWithOddNumber(value);
  }
}

첫 번째 경우는 모든 전제 조건을 먼저 확인하고 방해 요소를 제거한 다음 실행하려는 실제 코드로 진행하는 것처럼 더 많이 읽습니다. 따라서 실제로 실행하려는 수분이 많은 코드는 함수의 범위에 직접 속합니다. 그것은 기능에 관한 것입니다.
두 번째 경우는 두 경로가 어떤 조건에 따라 다른 것과 마찬가지로 기능과 관련이있는 유효한 코드로 실행되는 것처럼 보입니다. 따라서 이들은 서로 비슷한 범위 수준에 속합니다.


0

내가 본 적이있는 유일한 상황은 다음과 같은 코드입니다.

List<?> toList() {
    if (left != null && right != null) {
        Arrays.asList(merge(left, right));
    }
    if (left != null) {
        return Arrays.asList(left);
    }
    if (right != null) {
        return Arrays.asList(right);
    }
    return Arrays.asList();
}

여기에 분명히 잘못된 것이 있습니다. 문제는 return추가하거나 Arrays.asList제거 해야하는지 확실하지 않다는 것 입니다. 관련 방법을 자세히 검사하지 않으면이 문제를 해결할 수 없습니다. 항상 완전한 if-else 블록을 사용하여 이러한 모호성을 피할 수 있습니다. 이:

List<?> toList() {
    if (left != null && right != null) {
        Arrays.asList(merge(left, right));
    } else if (left != null) {
        return Arrays.asList(left);
    } else if (right != null) {
        return Arrays.asList(right);
    } else {
        return Arrays.asList();
    }
}

먼저 명시 적으로 반환을 추가하지 않으면 (정적으로 검사 된 언어로) 컴파일되지 않습니다 if .

일부 언어는 첫 번째 스타일도 허용하지 않습니다. 나는 엄격하게 명령적인 맥락에서만 또는 긴 방법으로 전제 조건으로 사용하려고합니다.

List<?> toList() {
    if (left == null || right == null) {
        throw new IllegalStateException()
    }
    // ...
    // long method
    // ...
}

-2

이와 같은 함수에서 3 개의 종료가 있으면 코드를 따르고 나쁜 연습을 수행하려고하면 실제로 혼란 스럽습니다.

기능에 대해 하나의 종료 점이 있으면 다른 것이 필요합니다.

var result;
if(check1(value)
{
    result = -1;
}
else if(check2(value))
{
    result = value;
}
else 
{
    result = 0;
}
return result;

실제로 더 나아가겠습니다.

var result = 0;
var c1 = check1(value);
var c2 = check2(value);

if(c1)
{
    result = -1;
}
else if(c2)
{
    result = value;
}

return result;

입력 유효성 검사에 대한 단일 초기 수익을 주장 할 수있을 정도로 공정합니다. 논리를 if에 넣는 경우 전체 대량을 래핑하지 마십시오.


3
이 스타일은 명시적인 리소스 관리 기능이있는 언어에서 비롯됩니다. 할당 된 리소스를 확보하려면 단일 지점이 필요했습니다. 자동 자원 관리를 사용하는 언어에서는 단일 리턴 포인트가 그렇게 중요하지 않습니다. 변수의 수와 범위를 줄이는 데 집중해야합니다.
Banthar

a : 질문에 언어가 지정되어 있지 않습니다. b : 나는 당신이 이것에 틀렸다고 생각합니다. 단일 반품에 대한 충분한 이유가 있습니다. 복잡성을 줄이고 가독성을 향상시킵니다.
Ewan

1
때로는 복잡성을 줄이고 때로는 증가시킵니다. 참조 wiki.c2.com/?ArrowAntiPattern
로버트 존슨

아니요. 항상 순환 복잡성을 줄인다고 생각합니다. 두 번째 예를 참조하십시오. check2 항상 실행
Ewan

조기 수익을 얻는 것은 일부 코드를 조건부로 이동하여 코드를 복잡하게하는 최적화입니다.
Ewan

-3

중복 else 문을 생략하는 것이 좋습니다. 코드 검토 또는 리팩토링에서 볼 때마다 작성자가 제어 흐름을 이해하고 코드에 버그가 있는지 궁금합니다.

저자가 else 서술문이 필요하다고 믿고 따라서 return 서술 문의 제어 흐름에 미치는 영향을 이해하지 못했다면 그러한 이해 부족은 버그,이 기능 및 일반적으로 주요한 원인입니다.


3
내가 당신에게 동의하는 동안 이것은 여론 조사가 아닙니다. 귀하의 주장을 뒷받침하십시오. 그렇지 않으면 유권자들이 당신을 친절하게 대하지 않을 것입니다.
candied_orange
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.