블록이 코드 복잡성을 증가 시키는가? [닫은]


18

다음은 매우 간단한 예 입니다. 이것은 언어 별 질문 일 필요는 없으며 , 함수를 작성할 수있는 다른 많은 방법과 그에 대한 변경을 무시할 것을 요청합니다. . 색깔은 유일한 유형입니다

string CanLeaveWithoutUmbrella()
{
    if(sky.Color.Equals(Color.Blue))
    {
        return "Yes you can";
    }
    else
    {
        return "No you can't";
    }
}

내가 만난 많은 사람들, ReSharper, 그리고이 사람 (이 의견은 내가 이걸 잠시 묻고 싶다고 생각 나게했다 else.)

(나는 대다수가 한 말을 기억할 수 없다.

string CanLeaveWithoutUmbrella()
{
    if(sky.Color.Equals(Color.Blue))
    {
        return "Yes you can";
    }
    return "No you can't";
}

질문 :else 블록을 포함하지 않아서 복잡성이 증가 합니까?

else두 블록의 코드가 직접적으로 관련되어 있다는 사실을 말함으로써 더 직접적인 상태의 의도에 감동하고 있습니다.

또한 논리의 미묘한 실수, 특히 나중에 코드를 수정 한 후에도 미묘한 실수를 예방할 수 있다는 것을 알았습니다.

단순화 된 예제의 변형을 생각해보십시오 (이것은 or의도적으로 단순화 된 예제이므로 연산자를 무시하십시오 ).

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color != Color.Blue)
    {
        return false;
    }
    return true;
}

누군가는 이제 if첫 번째 조건이 자신의 조건에 제약 조건을 적용하고 있음을 즉시 정확하게 인식하지 않고 첫 번째 예 이후 조건에 따라 새 블록을 추가 할 수 있습니다 .

는 IF else블록은 새로운 조건의 내용을 이동 이동하도록 강요 될 것이다 추가 누구든지 존재였다 else블록 (그들이 얼버무 어떻게 든 경우는 추론이 하나의 경우는하지 않는 코드는 도달 할 수없는 표시됩니다 if다른 제약) .

물론 특정 예를 정의해야하는 다른 방법이 있습니다. 이러한 모든 상황은 그러한 상황을 방지하지만 단지 예일뿐입니다.

내가 제시 한 예제의 길이는 이것의 시각적 측면을 왜곡시킬 수 있으므로, 괄호로 채워진 공간이 나머지 방법에 상대적으로 중요하지 않다고 가정하십시오.

else 블록의 생략에 동의하는 경우를 언급하는 것을 잊어 버렸고 if블록을 사용하여 null-check (또는 다른 경비원)와 같은 모든 후속 코드에 논리적으로 충족 되어야 하는 제약 조건을 적용 하는 경우를 언급했습니다. .


실제로 "반환"이있는 "if"문이있는 경우 모든 경우의 50 % 이상에서 "가드 블록"으로 볼 수 있습니다. 그래서 여러분의 오해가 여기에 "가드 블록 (guard block)"이 예외적 인 경우이고 귀하의 예가 일반적인 경우라고 생각합니다.
Doc Brown

2
@AssortedTrailmix : 위와 같은 경우 else조항을 작성할 때 더 읽기 쉽다는 것에 동의합니다 (제 취향에 따라 불필요한 괄호를 생략하면 훨씬 더 읽기 쉽습니다.) 위의 경우에 나는 return sky.Color == Color.Blue(if / else없이) 쓸 것이다 . bool과 다른 리턴 타입을 가진 예제는 아마도 이것을 더 명확하게 만들 것이다.
Doc Brown

1
위의 의견에서 지적했듯이 제안 된 예는 너무 단순하여 추측 답변을 초대합니다. "누군가 이제 새로운 if 블록을 추가 할 수 있습니다 ..."로 시작하는 질문의 일부에 대해서는 여러 번 요청 및 답변을 받았습니다. 예를 들어 여러 조건을 확인하는 방법은? 여러 질문이 연결되어 있습니다.
gnat

1
DRY 원칙과 관련하여 다른 블록이 무엇인지 알지 못합니다. DRY는 요청한 것보다 함수, 메소드 및 객체의 형태로 일반적으로 사용되는 코드에 대한 추상화 및 참조와 더 관련이 있습니다. 귀하의 질문은 코드 가독성 및 관습 또는 관용구와 관련이 있습니다.
Shashank Gupta

2
재개 투표. 이 질문에 대한 정답은 특히 의견 기반이 아닙니다. 질문에 주어진 예가 부적절하면 편집하여 그것을 개선하면 실제로는 사실이 아닌 이유로 닫히기로 투표하지 않는 것이 합리적입니다.
Michael Shaw

답변:


27

내 견해로는 명시 적 else 블록이 바람직합니다. 내가 이것을 볼 때 :

if (sky.Color != Blue) {
   ...
}  else {
   ...
}

나는 상호 배타적 인 옵션을 다루고 있음을 알고 있습니다. if 블록 내부의 내용을 읽을 필요가 없습니다.

내가 이것을 볼 때 :

if (sky.Color != Blue) {
   ...
} 
return false;

언뜻보기에 상관없이 false를 반환하고 하늘이 푸른 색이 아닌 선택적 부작용이있는 것으로 보입니다.

보시다시피, 두 번째 코드의 구조는 코드가 실제로하는 것을 반영하지 않습니다. 그 나쁜. 대신 논리 구조를 반영하는 첫 번째 옵션을 선택하십시오. 논리 흐름을 알기 위해 각 블록에서 리턴 / 브레이크 / 계속 논리를 확인하고 싶지 않습니다.

누군가가 다른 사람을 선호하는 이유는 저에게 미스터리입니다. 누군가가 반대 입장을 취하게되면 저의 대답이 밝아 질 것입니다.

else 블록의 생략에 동의하는 경우를 언급하지 않았으며 if 블록을 사용하여 null-check (또는 다른 경비원)와 같은 모든 다음 코드에 대해 논리적으로 충족되어야하는 제약 조건을 적용하는 경우를 언급했습니다. ).

나는 당신이 행복한 길을 떠난 경우 경비 조건이 괜찮다고 말합니다. 일반 실행이 계속되지 않는 오류 또는 오류가 발생했습니다. 이 경우 예외를 발생 시키거나 오류 코드를 반환하거나 언어에 적합한 것을 반환해야합니다.

이 답변의 원래 버전 직후에 내 프로젝트에서 이와 같은 코드가 발견되었습니다.

Foobar merge(Foobar left, Foobar right) {
   if(!left.isEmpty()) {
      if(!right.isEmpty()) {
          Foobar merged = // construct merged Foobar
          // missing return merged statement
      }
      return left;
   }
   return right;
}

반품을 다른 곳에 넣지 않음으로써 반품 명세서가 누락되었다는 사실을 간과했습니다. 다른 방법을 사용했다면 코드는 컴파일되지 않았을 것입니다. (물론이 코드로 작성된 테스트는이 문제를 감지하지 못하는 것이 상당히 나빴습니다.)


그것에 대해 내 생각을 요약하면, 내가 이것처럼 생각하는 유일한 사람이 아니라서 다행입니다. else블록을 제거하는 것을 선호하는 프로그래머, 도구 및 IDE와 함께 작업합니다 (코드베이스에 대한 규칙과 일치하도록 강제해야 할 때 귀찮을 수 있습니다). 나도 여기서 반점을보고 싶다.
Selali Adobor

1
나는 이것에 달려 있습니다. OP가 언급 한 미묘한 논리 오류를 유발할 가능성이 적기 때문에 명시 적 인 다른 것이 당신에게 많은 것을 얻지 못하는 경우가 있습니다. 그러나 대부분 동의합니다. 때로는 } // else다른 사람이 일반적으로 둘 사이의 타협으로가는 곳 과 같은 것을 할 것 입니다.
Telastyn

3
@ Telastyn,하지만 다른 것을 건너 뛰면 무엇을 얻습니까? 나는 많은 상황에서 그 혜택이 매우 적다는 데 동의하지만, 약간의 이익을 위해서라도 그만한 가치가 있다고 생각합니다. 분명히 코드가 될 수있을 때 주석에 무언가를 넣는 것은 기괴한 것 같습니다.
Winston Ewert

2
OP와 같은 오해가 있다고 생각합니다. "return"이있는 "if"는 대부분 가드 블록으로 간주 될 수 있으므로 예외적 인 경우에 대해 논의하고 있지만 가드 블록의 경우가 많을수록 IMHO가 더 읽기 쉽습니다. "그밖에".
Doc Brown

... 그래서 당신이 쓴 것은 잘못이 아니지만, IMHO는 당신이 얻은 많은 공감대 가치가 없습니다.
Doc Brown

23

else내가 찾은 블록 을 제거하는 주된 이유 는 과도한 들여 쓰기입니다. else블록의 단락은 훨씬 더 평평한 코드 구조를 가능하게합니다.

많은 if진술은 본질적으로 경비원입니다. 그들은 널 포인터의 역 참조와 다른 "이것을하지 마라!" 오류. 그리고 그들은 현재 기능을 빠르게 종료시킵니다. 예를 들면 다음과 같습니다.

if (obj === NULL) {
    return NULL;
}
// further processing that now can assume obj attributes are set

이제 else절이 매우 합리적 인 것처럼 보일 수 있습니다.

if (obj === NULL) {
    return NULL;
}
else if (obj.color != Blue) {
   // handle case that object is not blue
}

사실, 그것이 당신이하는 모든 일이라면, 위의 예보다 훨씬 들여 쓰기가 아닙니다. 그러나 이것은 실제 다중 레벨 데이터 구조 (JSON, HTML 및 XML과 같은 공통 형식을 처리 할 때 항상 발생하는 조건)로 거의 수행하지 않습니다. 따라서 주어진 HTML 노드의 모든 자식 텍스트를 수정하려면 다음과 같이 말합니다.

elements = tree.xpath(".//p");
if (elements === NULL) {
    return NULL;
}
p = elements[0]
if ((p.children === NULL) or (p.children.length == 0)) {
    return NULL;
}
for (c in p.children) {
    c.text = c.text.toUpperCase();
}
return p;

경비원은 코드의 들여 쓰기를 증가시키지 않습니다. else조항을 사용하면 모든 실제 작업이 오른쪽으로 이동하기 시작합니다.

elements = tree.xpath(".//p");
if (elements === NULL) {
    return NULL;
}
else {
    p = elements[0]
    if ((p.children === NULL) or (p.children.length == 0)) {
        return NULL;
    }
    else {
        for (c in p.children) {
            c.text = c.text.toUpperCase();
        }
        return p;
    }
}

이것은 중요한 오른쪽 운동을 시작하고 있으며, 두 가지 가드 조건 만있는 아주 간단한 예입니다. 모든 추가 경비는 또 다른 들여 쓰기 수준을 추가합니다. XML 처리를 작성했거나 자세한 JSON 피드를 사용하는 실제 코드는 3, 4 개 이상의 가드 조건을 쉽게 쌓을 수 있습니다. 항상을 사용하면 else4, 5 또는 6 레벨이 들여 쓰기됩니다. 그리고 이는 코드 복잡성에 기여하고, 무엇을 정렬하는지 이해하는 데 더 많은 시간을 소비합니다. 빠르고 평평한 조기 출구 스타일은 그러한 "과도한 구조"를 없애고 더 단순 해 보입니다.

부록 이 답변에 대한 의견은 NULL / 빈 처리가 가드 조건의 유일한 이유 가 아니라는 것이 분명하지 않다는 것을 깨달았습니다 . 거의 없습니다! 이 간단한 예제는 NULL / 빈 처리에 중점을두고 다른 이유를 포함하지 않지만, 예를 들어 XML 및 AST와 같은 심층 구조를 검색하여 "관련된"컨텐츠를 검색하는 경우 종종 관련없는 노드와 관심없는 것을 제거하기위한 일련의 특정 테스트가 있습니다. 사례. 일련의 "이 노드가 관련이없는 경우 리턴"테스트는 NULL / 빈 가드와 같은 종류의 오른쪽 드리프트를 유발합니다. 실제로, 후속 관련성 테스트는 검색된 해당 데이터 구조에 대한 NULL / 빈 가드와 자주 결합됩니다 (오른쪽 드리프트에 대한 이중 약점).


2
이것은 상세하고 유효한 답변이지만 질문에 이것을 추가 할 것입니다 (처음에는 마음을 벗어났습니다). 널 체크 (또는 다른 가드)와 같은 모든 다음 코드에 대해 논리적으로 충족 되어야 하는 제약 조건을 적용하기 else위해 if블록을 사용할 때 항상 불필요한 블록을 찾는 "예외" 있습니다 .
Selali Adobor

@AssortedTrailmix 나는 당신이 규칙적으로 문을 조기에 종료 할 수있는 경우 then(예 : 연속 가드 문)를 제외하면 특정 else절 을 피할 특별한 가치가 없다고 생각 합니다. 실제로, 그것은 명확성을 감소시키고 잠재적으로 위험 할 수있다 (존재해야 할 대안 적 구조를 간단히 언급하지 않음으로써).
Jonathan Eunice

나는 당신이 행복한 길을 떠난 후에 가드 조건을 사용하여 방출하는 것이 합리적이라고 생각합니다. 그러나 XML / JSON을 처리하는 경우 먼저 스키마에 대해 입력의 유효성을 검사하면 더 나은 오류 메시지와 더 깨끗한 코드가 표시됩니다.
Winston Ewert

이것은 피해야 할 경우에 대한 완벽한 설명입니다.
Chris Schiffhauer

2
대신 바보 NULL을 생성하지 않도록 API를 래핑했습니다.
Winston Ewert

4

내가 이것을 볼 때 :

if(something){
    return lamp;
}
return table;

나는 일반적인 관용구를 참조하십시오 . 일반적인 패턴 은 내 머리 속에 다음과 같이 번역됩니다.

if(something){
    return lamp;
}
else return table;

이것은 프로그래밍에서 매우 일반적인 관용구이기 때문에 이런 종류의 코드를 보는 데 익숙한 프로그래머는 그것을 볼 때마다 머리에 암시 적 '번역'을 가지고 있으며, 이는 그것을 올바른 의미로 '변환'합니다.

나는 명시 적으로 필요하지 않습니다 else. 제 머리는 패턴 을 전체적으로 인식했기 때문에 저에게 ' 넣어 놓았습니다' . 전체 공통 패턴의 의미를 이해하므로 명확하게하기 위해 작은 세부 사항이 필요하지 않습니다.

예를 들어 다음 텍스트를 읽으십시오.

여기에 이미지 설명을 입력하십시오

이거 읽었 어?

나는 봄 날에 파리를 좋아한다

그렇다면 틀린 것입니다. 글을 다시 읽으세요. 실제로 쓰여진 것은

나는 파리를 사랑 봄

본문 에는 두 개의 "the"단어가 있습니다. 그렇다면 왜 둘 중 하나를 그리워 했습니까?

두뇌가 공통된 패턴을 보고 즉시 알려진 의미로 번역했기 때문입니다. 의미를 파악하기 위해 세부 사항을 자세히 읽을 필요가 없었습니다. 이미 패턴 을 보았을 때 패턴 을 인식했기 때문 입니다. 이것이 추가 "the "를 무시한 이유 입니다.

위의 관용구와 같은 것. 코드를 많이 읽었습니다 프로그래머는이보고에 사용되는 패턴 의, if(something) {return x;} return y;. 그들은 else뇌가 '그들을 위해 그것을 거기에 두기 '때문에 의미를 이해하기 위해 explciit 가 필요하지 않습니다 . 그들은 알려진 의미를 가진 패턴으로 인식합니다 : "닫는 중괄호 이후 else의 것은 이전 의 암시 적 으로 만 실행됩니다 if".

내 의견으로는, else불필요합니다. 그러나 더 읽기 쉬운 것을 사용하십시오.


2

코드에서 시각적 혼란을 더하므로 복잡성을 더할 수 있습니다.

그러나 반대의 경우도 마찬가지입니다. 코드 길이를 줄이기 위해 과도한 리팩토링은 복잡성을 추가 할 수도 있습니다 (물론 간단한 경우는 아닙니다).

나는 else블록이 의도 한다고 진술에 동의합니다 . 그러나 내 결론은 다릅니다.이를 달성하기 위해 코드에 복잡성을 추가하기 때문입니다.

나는 당신이 논리의 미묘한 실수를 예방할 수 있다는 당신의 진술에 동의하지 않습니다.

예를 들어 보겠습니다. 이제 하늘이 파란색이고 습도가 높거나 습도가 높지 않거나 하늘이 빨간색 인 경우에만 true를 반환해야합니다. 그러나, 당신은 하나의 버팀대를 착각하여 그것을 잘못 놓았습니다 else:

bool CanLeaveWithoutUmbrella() {
    if(sky.Color == Color.Blue)
    {
        if (sky.Humidity == Humidity.High) 
        {
            return true;
        } 
    else if (sky.Humidity != Humidity.High)
    {
        return true;
    } else if (sky.Color == Color.Red) 
    {
            return true;
    }
    } else 
    {
       return false;
    }
}

우리는 생산 코드에서 이런 종류의 바보 같은 실수를 모두 보았고 탐지하기가 매우 어려울 수 있습니다.

나는 다음을 제안 할 것이다 :

기본값이 인 것을 한 눈에 볼 수 있기 때문에 이것을 읽기가 더 쉽습니다 false.

또한 새로운 절을 삽입하기 위해 블록의 내용을 강제로 이동한다는 생각은 오류가 발생하기 쉽습니다. 이것은 어떻게 든 개방 / 폐쇄 원칙 과 관련 이 있습니다 (코드는 확장을 위해 열려 있어야하지만 수정을 위해 닫아야합니다). 기존 코드를 수정해야합니다 (예 : 코더가 중괄호 균형에 오류를 일으킬 수 있음).

마지막으로, 당신은 사람들이 당신이 옳다고 생각하는 미리 정해진 방식으로 답변하도록 이끌고 있습니다. 예를 들어, 당신은 매우 간단한 예를 제시하지만 나중에 사람들은 그것을 고려해서는 안된다고 진술합니다. 그러나 예제의 단순성은 전체 질문에 영향을 미칩니다.

  • 사례가 제시된 사례만큼 단순하다면 내 대답은 if else절을 전혀 생략하는 것입니다.

    반환 (sky.Color == Color.Blue);

  • 다양한 조항으로 사건이 조금 더 복잡하다면 다음과 같이 대답합니다.

    bool CanLeaveWithoutUmbrella () {

    if(sky.Color == Color.Blue && sky.Humidity == Humidity.High) {
        return true;
    } else if (sky.Humidity != Humidity.High) {
        return true;
    } else if (sky.Color == Color.Red) {
        return true;
    }
    
    return false;
    

    }

  • 대소 문자가 복잡하고 모든 절이 true를 반환하지는 않지만 (두 개를 반환 할 수도 있음) 중단 점 또는 loggin 명령을 소개하고 싶은 경우 다음과 같이 고려하십시오.

    double CanLeaveWithoutUmbrella() {
    
        double risk = 0.0;
    
        if(sky.Color == Color.Blue && sky.Humidity == Humidity.High) {
            risk = 1.0;
        } else if (sky.Humidity != Humidity.High) {
            risk = 0.5;
        } else if (sky.Color == Color.Red) {
            risk = 0.8;
        }
    
        Log("value of risk:" + risk);
        return risk;
    

    }

  • 우리가 무언가를 반환하는 메소드에 대해 이야기하고 있지 않지만 대신 변수를 설정하거나 메소드를 호출하는 if-else 블록이라면 마지막 else절을 포함시킬 것 입니다.

따라서 예제의 단순성도 고려해야합니다.


나는 당신이 다른 사람들이했던 것과 같은 실수를 저지르고 예제 너무 많이 변형하여 새로운 문제를 검토하고 있다고 생각하지만, 잠시 시간 을 내서 당신의 예제를 얻는 과정을 조사해야합니다. 나에게 유효합니다. 그리고 참고로, 이것은 개방 / 폐쇄 원칙 과 관련이 없습니다 . 이 관용구를 적용하기에는 범위가 너무 좁습니다. 그러나 (의 필요성을 제거하여 문제의 모든 측면을 제거하는 것보다 높은 수준의 응용 프로그램을주의하는 것이 흥미 있는 기능의 수정을).
Selali Adobor

1
그러나이 단순화 된 예제 추가 절을 수정하거나 작성 방법을 수정하거나 변경할 수없는 경우이 질문은 정확한 코드 행에만 관련되며 더 이상 일반적인 질문이 아니라는 점을 고려해야합니다. 이 경우, 처음부터 복잡성이 없기 때문에 else 절을 ​​복잡하게 추가하거나 제거하지 않습니다. 그러나 새로운 조항이 추가 될 수 있다는 아이디어를 제시하는 사람이므로 공정하지 않습니다.
FranMowinckel

그리고 내가 언급했듯이, 나는 이것이 잘 알려진 개방 / 폐쇄 원칙과 관련이 있다고 말하지는 않습니다. 사람들이 제안한대로 코드를 수정하도록 유도하는 아이디어는 오류가 발생하기 쉽다고 생각합니다. 나는 단지이 아이디어를 그 원칙에서 취하고 있습니다.
FranMowinckel

편집 내용을 유지하면 좋은
결과를 얻을 수 있습니다.

1

설명 된 경우가 더 복잡한 경우도 있지만 대부분의 실제 상황에서는 그다지 중요하지 않습니다.

몇 년 전 항공 산업에 사용 된 소프트웨어 (인증 프로세스를 거쳐야 함)를 작업 할 때 문제가 제기되었습니다. 코드 복잡성을 증가시키면서 '다른'진술에 재정적 비용이 발생하는 것으로 나타났습니다.

이유는 다음과 같습니다.

'else'의 존재는 'if'나 'else'가 아닌 암시적인 세 번째 경우를 평가해야했습니다. 당신은 WTF를 생각하고있을 것입니다.

설명은 다음과 같습니다 ...

논리적 관점에서는 'if'와 'else'의 두 가지 옵션 만 있습니다. 그러나 우리가 인증 한 것은 컴파일러가 생성 한 객체 파일이었습니다. 따라서 'else'가 발생하면 생성 된 분기 코드가 올 바르고 신비한 3 번째 경로가 나타나지 않았 음을 확인하는 분석을 수행해야했습니다.

'if'만 있고 'else'가없는 경우와 대조하십시오. 이 경우 'if'경로와 'non-if'경로의 두 가지 옵션 만 있습니다. 평가할 두 가지 경우가 세 가지보다 덜 복잡합니다.

도움이 되었기를 바랍니다.


객체 파일에서 두 경우 모두 동일한 JMP로 렌더링되지 않습니까?
Winston Ewert

@WinstonEwert-하나는 동일 할 것으로 기대합니다. 그러나 렌더링 대상과 렌더링 대상은 중첩 정도에 관계없이 서로 다른 두 가지입니다.
Sparky

(SE가 내 의견을 먹은 것 같아요 ...) 이것은 매우 흥미로운 답변입니다. 나는 그것이 노출하는 경우가 다소 틈새 시장이라고 말하고 싶지만 있지만, 그것은 몇 가지 도구가 둘 사이의 차이 (내가, 복잡성을 참조 흐름 차이를 측정하지 않을 것이다 기반으로 메트릭 심지어 가장 일반적인 코드를 찾을 것이라고 쇼를한다.
Selali을 Adobor

1

코드의 가장 중요한 목표는 커밋 된 것으로 이해 하는 것입니다 (쉽게 리팩토링되는 것이 아니라 유용하지만 덜 중요합니다). 약간 더 복잡한 Python 예제를 사용하여이를 설명 할 수 있습니다.

def value(key):
    if key == FROB:
        return FOO
    elif key == NIK:
        return BAR
    [...]
    else:
        return BAZ

이것은 매우 명확합니다. case문장 또는 사전 조회와 동일합니다 return foo == bar.

KEY_VALUES = {FROB: FOO, NIK: BAR, [...]}
DEFAULT_VALUE = BAZ

def value(key):
    return KEY_VALUES.get(key, default=DEFAULT_VALUE)

토큰 수가 더 많더라도 (두 개의 키 / 값 쌍이있는 원래 코드의 경우 32 대 24) 함수의 의미는 이제 명시 적입니다. 이는 단지 조회 일뿐입니다.

(이것은 리팩토링에 영향을 미칩니다-만약에 부작용을 원한다면 key == NIK, 세 가지 선택이 있습니다 :

  1. if/ elif/ else스타일로 되돌리고 key == NIK케이스에 한 줄을 삽입하십시오 .
  2. 조회 결과를 값에 저장하고 리턴하기 전에 특수한 경우 ifvalue대한 명령문을 추가하십시오 .
  3. 발신자 에게 if진술서를 작성하십시오 .

이제 조회 기능이 강력한 단순화 인 이유를 알 수 있습니다. 세 번째 옵션은 가장 간단한 솔루션입니다. 첫 번째 옵션보다 차이가 작을 뿐 아니라 단 한 가지만 수행 할 수있는 유일한value 옵션 이기 때문입니다. )

OP의 코드로 돌아가서, 이것이 else문장 의 복잡성에 대한 가이드 역할을 할 수 있다고 생각 합니다. 명시적인 else문장을 가진 코드 는 객관적으로 더 복잡하지만 , 코드 의 의미 를 명확하고 단순 하게 만드는 것이 더 중요 합니다. 모든 코드는 다른 의미를 갖기 때문에 사례별로 답변해야합니다.


간단한 스위치 케이스를 룩업 테이블에서 다시 작성하는 것이 좋습니다. 그리고 나는 그것이 선호하는 파이썬 관용구라는 것을 알고 있습니다. 그러나 실제로는 다 방향 조건 (일명 case또는 switch문장)이 덜 균일 하다는 것을 종종 발견합니다 . 여러 옵션은 단순한 가치 반환이지만 한두 가지 경우에 추가 처리가 필요합니다. 검색 전략이 적합하지 않다는 것을 발견하면 완전히 다른 if elif... else구문 으로 다시 작성해야 합니다.
Jonathan Eunice

그렇기 때문에 조회는 일반적으로 하나의 라이너 또는 함수이어야한다고 생각하므로 조회 결과 주위의 논리가 처음부터 조회하는 논리와 분리됩니다.
l0b0

이 질문은 분명히 언급하고 명심해야 할 질문에 대한 높은 수준의 견해를 가져다 주지만, 자신의 입장에서 자유롭게 확장 할 수있는 경우에는 충분한 제약이 있다고 생각합니다.
Selali Adobor

1

나는 그것이 if당신이 사용 하는 진술의 종류에 달려 있다고 생각합니다 . 일부 if명령문은 표현식으로 볼 수 있으며 다른 명령문은 제어 흐름 명령문으로 만 볼 수 있습니다.

당신의 예는 나에게 표현처럼 보입니다. C와 같은 언어에서 삼항 연산자?:if명령문 과 매우 유사 하지만 표현식이므로 else 부분은 생략 할 수 없습니다. 표현식은 항상 값을 가져야하므로 표현식에서 else 분기를 생략 할 수 없습니다.

삼항 연산자를 사용하면 예제는 다음과 같습니다.

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue)
               ? true
               : false;
}

그러나 부울 식이므로 실제로는 단순화해야합니다.

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue);
}

명시 적 else 절을 ​​포함하면 의도가 더 명확 해집니다. 경비원은 어떤 상황에서는 의미가 있지만, 예외적이거나 특이하지 않은 두 가지 가능한 값 중에서 선택하면 도움이되지 않습니다.


+1, OP는 IMHO가 위에서 보여준 방식으로 더 잘 작성되어야하는 병리학 적 사례를 논의하고 있습니다. "반환" 이있는 "if"에 대한 가장 빈번한 사례 제 경험에 "가드"사례입니다.
Doc Brown

왜 이런 일이 계속 발생하는지 모르겠지만 사람들은 질문의 첫 번째 단락을 계속 무시합니다. 사실, 당신은 내가 명시 적으로 사용하지 말라고 정확한 코드 줄을 사용하고 있습니다. I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Selali Adobor

그리고 마지막 질문을 다시 읽으면 질문의 의도를 오해하는 것 같습니다.
Selali Adobor

실제로 단순화하기를 간절히 구한 예를 사용했습니다. 첫 번째 단락을 읽고주의를 기울였습니다. 그래서 특정 예제를 수행하는 가장 좋은 방법으로 직접 건너 뛰는 대신 첫 번째 코드 블록이 있습니다. 본인 (또는 다른 사람)이 귀하의 질문의 의도를 잘못 이해했다고 생각되면 의도를 명확히하기 위해 편집해야합니다.
Michael Shaw

나는 그것을 편집 할 것이지만, 당신이하고 있는 정확한 코드 줄을 사용하고 "하지 마십시오"라고 말하면 더 명확하게 만드는 방법을 알지 못합니다 . 코드를 다시 작성하고 질문을 제거 할 수있는 무한한 방법이 있습니다. 모든 것을 다룰 수는 없지만 많은 사람들이 내 진술을 이해하고 존중했습니다 ...
Selali Adobor

1

이 주장에서 고려해야 할 또 다른 사항은 각 접근 방식이 장려하는 습관입니다.

  • if / else는 브랜치 측면에서 코딩 샘플을 재구성합니다. 당신이 말했듯이, 때로는이 명시 성이 좋습니다. 실제로 두 가지 가능한 실행 경로가 있으며이를 강조하고 싶습니다.

  • 다른 경우에는 실행 경로가 하나 뿐이며 프로그래머가 염려해야 할 모든 예외 사항이 있습니다. 언급했듯이 가드 조항이 좋습니다.

  • 물론 3 개 이상의 경우 (n)가 있으며, 일반적으로 if / else 체인을 포기하고 case / switch 문 또는 다형성을 사용하도록 권장합니다.

여기서 명심해야 할 기본 원칙은 방법이나 기능에 단 하나의 목적이 있어야한다는 것입니다. if / else 또는 switch 문이있는 코드는 분기 전용입니다. 따라서 터무니없이 짧아야하며 (4 줄) 올바른 방법으로 만 위임하거나 예와 같이 명확한 결과를 생성해야합니다. 코드가 너무 짧으면 여러 번의 반환을 놓치기가 어렵습니다. :) "고토가 해로운 것으로 간주 됨"과 마찬가지로 모든 분기 문은 남용 될 수 있으므로 다른 문에 중요한 생각을하는 것이 일반적으로 가치가 있습니다. 언급했듯이 else 블록 만 있으면 코드가 덩어리 질 수 있습니다. 당신과 당신의 팀은 그것에 대한 느낌을 결정해야합니다.

도구가 실제로 불평하는 것은 if 블록 안에 리턴 / 브레이크 / 투사가 있다는 사실입니다. 그래서, 당신은 가드 조항을 작성했지만 망쳤습니다. 도구를 행복하게 만들거나 제안을 수락하지 않고 제안을 "입력"할 수 있습니다.


나는 도구가 if가 하나의 패턴에 맞 자마자 가드 절이라고 생각할 수도 있다는 사실에 대해서는 결코 생각하지 않았다. 아마도 그럴 수도있다. 그리고 그것이 하나의 "지지자 그룹"에 대한 이유를 설명하지 않는다
Selali Adobor

@AssortedTrailmix 맞아, 당신은 else의미 론적으로 생각하고 있습니다 . 코드 분석 도구는 일반적으로 제어 흐름의 오해를 강조하기 때문에 관련없는 명령을 찾습니다. 그 else후의 if반환은 낭비 된 비트입니다. if(1 == 1)종종 같은 종류의 경고를 유발합니다.
Steve Jackson

1

대답은 그것이 달려 있다고 생각합니다. 코드 샘플 (간접적으로 지적한 바와 같이)은 단순히 조건의 평가를 반환하는 것으로, 명백히 알 수 있듯이 단일 표현식으로 가장 잘 표현됩니다.

else 조건을 추가하여 코드를 명확하게하거나 모호하게하는지 확인하려면 IF / ELSE가 무엇을 나타내는 지 결정해야합니다. (예와 같이) 표현식입니까? 그렇다면 해당 표현을 추출하여 사용해야합니다. 가드 상태입니까? 그렇다면 ELSE는 불필요하고 오해의 소지가 있습니다. 두 분기가 동일하게 보이기 때문입니다. 절차 적 다형성의 예입니까? 추출하십시오. 전제 조건을 설정합니까? 그렇다면 그것은 필수적 일 수 있습니다.

ELSE를 포함 시키거나 분리하기로 결정하기 전에 ELSE가 나타내는 것이 무엇인지 결정하십시오.


0

대답은 약간 주관적입니다. else블록은 두 블록을 읽을 필요없이 전용 코드의 다른 블록에 코드가 실행의 한 블록을 설정하여 가독성을 도움이 될 수있다. 예를 들어 두 블록이 비교적 긴 경우에 유용합니다. ifs없이 elses를 갖는 것이 더 읽기 쉬운 경우가 있습니다. 다음 코드를 여러 if/else블록 과 비교하십시오 .

if(a == b) {
    if(c <= d) {
        if(e != f) {
            a.doSomeStuff();
            c.makeSomeThingsWith(b);
            f.undo(d, e);
            return 9;
        } else {
            e++;
            return 3;
        }
    } else {
        return 1;
    }
} else {
    return 0;
}

와:

if(a != b) {
    return 0;
}

if(c > d) {
    return 1;
}

if(e != f) {
    e++;
    return 3;
}

a.doSomeStuff();
c.makeSomeThingsWith(b);
f.undo(d, e);
return 9;

두 번째 코드 블록은 중첩 된 if명령문을 보호 절로 변경합니다 . 이렇게하면 들여 쓰기가 줄어들고 반환 조건 중 일부가 더 명확 해집니다 ( "if이면 a != b반환 0!").


내 예에 약간의 구멍이있을 수 있다는 의견에서 지적되었으므로 조금 더 살 살릴 것입니다.

더 읽기 쉽게하기 위해 여기에서 첫 번째 코드 블록을 작성할 수있었습니다.

if(a != b) {
    return 0;
} else if(c > d) {
    return 1;
} else if(e != f) {
    e++;
    return 3;
} else {
    a.doSomeStuff();
    c.makeSomeThingsWith(b);
    f.undo(d, e);
    return 9;
}

이 코드는 위의 두 블록과 동일하지만 몇 가지 문제가 있습니다. else절은 여기 에 연결 함께 if 문이. 위의 가드 절 버전에서 가드 절은 서로 독립적입니다. 새로운 것을 추가한다는 것은 단순히 if마지막 if논리와 나머지 논리 사이 에 새로운 것을 쓰는 것을 의미 합니다. 여기에서도 약간의인지 부하만으로도 가능합니다. 그러나 새 가드 절 이전에 몇 가지 추가 논리가 필요한 경우가 다릅니다. 진술이 독립적이었을 때, 우리는 할 수있었습니다 :

...
if(e != f) {
    e++;
    return 3;
}

g.doSomeLogicWith(a);

if(!g.isGood()) {
    return 4;
}

a.doSomeStuff();
...

연결된 if/else체인을 사용하면 쉽지 않습니다. 사실, 가장 쉬운 방법은 체인을 분리하여 가드 조항 버전처럼 보이게하는 것입니다.

그러나 실제로 이것은 의미가 있습니다. if/else약하게 사용 한다는 것은 부품 간의 관계를 의미합니다. 우리는 추측 할 수 a != b어떻게 든 관련이 c > dif/else연쇄 버전. 가드 조항 버전에서 실제로는 관련이 없다는 것을 알 수 있듯이 이러한 가정은 잘못된 것입니다. 즉, 재정렬 (어쩌면 쿼리 성능 최적화 또는 논리적 흐름 개선)과 같은 독립적 인 절로 더 많은 작업을 수행 할 수 있습니다. 우리는 과거를 지나면인지 적으로 무시할 수 있습니다. 에서 if/else체인, 내가 덜 확신 그 사이의 등가 관계 ab 나중에 다시 팝업되지 않습니다.

묵시적인 관계 else는 깊게 중첩 된 예제 코드에서도 분명하지만 다른 방식으로 나타납니다. else문은 그들이 부착되는 조건으로부터 분리되고, 그들에 관련된 역방향 조건문 순서 (제 else마지막에 부착되고 if, 마지막은 else제에 부착된다 if). 이것은 우리가 코드를 역 추적하여 뇌의 들여 쓰기를 줄 이도록하는인지 불협화음을 만듭니다. 그것은 많은 괄호를 일치시키는 것과 같습니다. 들여 쓰기가 잘못되면 더욱 악화됩니다. 선택적 괄호도 도움이되지 않습니다.

else 블록의 생략에 동의하는 경우를 언급하지 않았으며 if 블록을 사용하여 null-check (또는 다른 경비원)와 같은 모든 다음 코드에 대해 논리적으로 충족되어야하는 제약 조건을 적용하는 경우를 언급했습니다. ).

이것은 좋은 양보이지만 실제로 답변을 무효화하지는 않습니다. 코드 예제에서 다음과 같이 말할 수 있습니다.

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color != Color.Blue)
    {
        return false;
    }
    return true;
}

이와 같은 코드 작성에 대한 올바른 해석은 "하늘이 푸른 색이 아닌 경우에만 우산을 남겨 두어야합니다." 다음 코드를 실행하려면 하늘이 파란색이어야한다는 제약이 있습니다. 나는 당신의 양보의 정의를 충족 시켰습니다.

하늘색이 검은 색이면 맑은 밤이므로 우산이 필요하지 않다고 말하면 어떻게 될까요? (이를 작성하는 간단한 방법이 있지만 전체 예제를 작성하는 간단한 방법이 있습니다.)

bool CanLeaveWithoutUmbrella()
{
    if(sky.Color == Color.Blue)
    {
        return true;
    }

    if(sky.Color == Color.Black)
    {
        return true;
    }

    return false;
}

위의 더 큰 예에서와 같이, 나는 많은 생각없이 간단하게 새로운 절을 추가 할 수 있습니다. 에서 if/else, 특히 논리가 더 복잡한 경우 무언가를 엉망으로 만들지 않을 것이라고 확신 할 수 없습니다.

또한 간단한 예제도 그렇게 간단하지 않다는 것을 지적 할 것입니다. 이진이 아닌 값에 대한 검정은 반전 된 결과를 가진 해당 검정의 역수와 동일하지 않습니다.


1
질문에 대한 나의 편집은이 사건을 다룬다. null-check (또는 다른 가드)와 같은 모든 후속 코드에 대해 논리적으로 충족되어야하는 제약 조건이 else블록 의 생략이 가독성에 필수적 이라는 데 동의합니다 .
Selali Adobor

1
첫 번째 버전은 이해하기 어렵지만 if / else를 사용하여 이것이 필요하다고 생각하지 않습니다. 작성 방법을 참조하십시오 : gist.github.com/winstonewert/c9e0955bc22ce44930fb .
Winston Ewert

@WinstonEwert 의견에 대한 답변은 수정본을 참조하십시오.
cbojar

편집하면 몇 가지 유효한 점이 있습니다. 그리고 나는 가드 조항이 자리를 잡고 있다고 생각합니다. 그러나 일반적인 경우 else 절을 ​​선호합니다.
Winston Ewert

0

예를 너무 단순화했습니다. 어느 한 쪽 대안이 큰 상황에 따라 더 읽을 수있다 : 즉, 상기 조건의 두 가지 중 하나인지 여부에 따라 비정상 . 두 가지가 똑같이 가능성이있는 경우에 ...

void DoOneOfTwoThings(bool which)
{
    if (which)
        DoThingOne();
    else
        DoThingTwo();
}

...보다 읽기 쉽다 ...

void DoOneOfTwoThings(bool which)
{
    if (which) {
        DoThingOne();
        return;
    }
    DoThingTwo();
}

... 첫 번째는 평행 구조를 나타 내기 때문에 를 나타내고 두 번째는 그렇지 않은 입니다. 그러나 대부분의 기능이 하나의 작업에 전념하고 일부 전제 조건을 먼저 확인해야 할 때 ...

void ProcessInputOfTypeOne(String input)
{
    if (InputIsReallyTypeTwo(input)) {
        ProcessInputOfTypeTwo(input);
        return;
    }
    ProcessInputDefinitelyOfTypeOne(input);

}

...보다 읽기 쉽다 ...

void ProcessInputOfTypeOne(String input)
{
    if (InputIsReallyTypeTwo(input))
        ProcessInputOfTypeTwo(input);
    else
        ProcessInputDefinitelyOfTypeOne(input);
}

... 병렬 구조를 의도적으로 설정 하지 않으면 미래의 독자에게 "실제로 유형 2"인 입력을받는 것이 이상 하다는 단서가 되기 때문 입니다. (실제에서는 ProcessInputDefinitelyOfTypeOne별도의 기능이 아니므로 차이가 훨씬 더 커질 것입니다.)


두 번째 경우에 대해보다 구체적인 예를 선택하는 것이 좋습니다. 이에 대한 나의 즉각적인 반응은 "만약 당신이 계속해서 그것을 처리한다면 그것은 비정상이 될 수 없습니다."
Winston Ewert

@WinstonEwert 비정상 은 아마도 잘못된 용어 일 것입니다. 그러나 Zack에 동의합니다. 기본 (case1)과 하나의 예외가있는 경우» if-> 예외를 수행하고 복귀 초기 전략 으로 남겨 둡니다«로 작성해야합니다 . 기본값에 더 많은 검사가 포함 된 코드를 생각 하면 예외적 인 경우를 먼저 처리하는 것이 더 빠릅니다.
토마스 정크

이것은 내가 이야기했던 사건에 빠진 것 같습니다 when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards). 논리적으로 모든 작업 ProcessInputOfTypeOne은 사실에 달려 !InputIsReallyTypeTwo(input)있기 때문에 정상적인 처리 외부에서 처리해야합니다. 일반적으로 유사성이 더 분명하게 ProcessInputOfTypeTwo(input);throw ICan'tProccessThatException것입니다.
Selali Adobor

@WinstonEwert 아마도 더 잘 표현했을 것입니다. 예를 들어 CSS에서 문자 시퀀스 " u+"는 일반적으로 "유니 코드 범위"토큰을 소개하지만 다음 문자가 16 진수가 아닌 경우, 토큰 화기를 직접 코딩 할 때 많은 상황이 발생한다고 생각했습니다. 그런 다음 대신 'u'를 식별자로 백업하고 처리해야합니다. 따라서 메인 스캐너 루프는 "유니 코드 범위 토큰 스캔"을 다소 부정확하게 전달하고 "...이 특별한 특수한 경우 백업을 수행 한 다음 대신 스캔-식별자 서브 루틴을 호출합니다."로 시작합니다.
zwol

@AssortedTrailmix 내가 생각했던 예제가 예외가 사용되지 않을 수있는 레거시 코드베이스에서 나온 것이 아니더라도 이것은 오류 조건 이 아니며 호출하는 코드 ProcessInputOfTypeOne가 때로는 부정확하지만 빠른 검사를 사용하는 것입니다. 대신 타입 2를 잡습니다.
zwol

0

예, else 절이 중복 되어 복잡성이 증가 합니다. 따라서 코드를 간결하고 의미있게 유지하는 것이 좋습니다. 중복 this한정자 (Java, C ++, C #)를 생략하고 return명령문에서 괄호를 생략 하는 것과 같은 코드 에 있습니다.

좋은 프로그래머는 "무엇을 추가 할 수 있습니까?"라고 생각합니다. 훌륭한 프로그래머는 "무엇을 제거 할 수 있습니까?"라고 생각합니다.


1
else블록 의 생략을 볼 때 , 하나의 라이너로 설명되면 (종종 그렇지는 않음) 종종 이런 종류의 추론과 관련이 있으므로 "기본"인수를 제시하기 위해 +1이지만, 그렇지 않습니다. 그것이 충분히 강력한 추론이라고 생각하십시오. A good programmer things "what can I add?" while a great programmer things "what can I remove?".많은 사람들이 신중하게 고려하지 않고 초과 확장한다는 일반적인 속담 인 것 같습니다. 저는 개인적으로 이러한 사례 중 하나라고 생각합니다.
Selali Adobor

네, 당신의 질문을 읽으면서 나는 당신이 이미 당신의 마음을 구성했다고 생각했지만 확인을 원했습니다. 이 답변은 분명히 당신이 원하는 것이 아니지만 때로는 동의하지 않는 의견을 고려하는 것이 중요합니다. 어쩌면 뭔가가 있습니까?
vidstige

-1

이 사건에 대해 마음이 바뀌는 것을 알았습니다.

약 1 년 전에이 질문을 받았을 때, 나는 다음과 같이 대답했을 것입니다.

» 메소드에는 entry한 가지만 있어야합니다 exit«이것을 염두에두고 코드를 리팩터링하는 것이 좋습니다.

bool CanLeaveWithoutUmbrella()
{
    bool result

    if(sky.Color == Color.Blue)
    {
        result = true;
    }
    else
    {
        result = false;
    }

    return result;
}

의사 소통의 관점에서 해야 할 일 이 분명 합니다. If어떤 경우 가 있습니다. 결과를 한 가지 방법 으로 else 조작하고 다른 방법 으로 결과를 조작합니다 .

그러나 이것은 불필요 else 합니다. 는 else표현 기본-경우 . 두 번째 단계에서는 리팩토링 할 수 있습니다.

bool CanLeaveWithoutUmbrella()
{
    bool result=false

    if(sky.Color == Color.Blue)
    {
        result = true;
    }
    return result;
}

그렇다면 왜 임시 변수를 사용 하는가? 리팩토링의 다음 단계는 다음과 같습니다.

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue)
    {
        return true;
    }
    return false;
}

그래서 이것은 그것이하는 일에서 분명 합니다. 한 걸음 더 나아갈 것입니다.

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue) return true;
    return false;
}

또는 희미한 마음으로 :

bool CanLeaveWithoutUmbrella()
{

    if(sky.Color == Color.Blue) { return true; }
    return false;
}

다음과 같이 읽을 수 있습니다.»CanLeaveWithoutUmbrella는 bool을 반환합니다 . 특별한 일이 없으면 false를 반환합니다. «처음부터 끝까지 읽습니다.

그런 다음 조건을 "구문 분석"합니다.»조건이 충족되면 사실 당신은 완전히 수« 건너 조건을이 기능이에 무엇을, 이해 기본 - 케이스. 당신의 눈과 마음은 오른쪽에있는 부분을 읽지 않고 왼쪽에있는 글자들을 훑어 봐야합니다.

두 블록의 코드가 직접적으로 관련되어 있다는 사실을 언급함으로써 더 직접적으로 언급하려는 인상을 받고 있습니다.

응 그게 맞아. 그러나 그 정보는 중복 적 이다. 기본 사례와 하나의 "예외"가 if있으며이 예외는- 문장에 포함됩니다.

복잡한 구조를 다룰 때 나는 다른 전략을 선택하고 if추한 형제 switch를 생략합니다 .


+1 이중화는 복잡성뿐만 아니라 혼란의 증가를 분명히 보여줍니다. 중복으로 인해 항상 이와 같이 작성된 코드를 다시 확인해야한다는 것을 알고 있습니다.
dietbuddha

1
사례가 시작된 후 사례를 제거 So this is clear in what it does...하고 확장 할 수 있습니까? (이전의 사례들도 의문의 여지가 있습니다). 나는 이것이 우리가 묻는 질문이기 때문에 이것을 말합니다. else 블록을 생략하고 자신의 입장을 말했으며 실제로 더 자세한 설명이 필요한 입장입니다 (이 질문을하는 입장). 질문에서 :I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Selali Adobor

트윗 담아 가기 정확히 무엇을 정교화해야합니까? 귀하의 초기 질문은»다른 블록이 코드 복잡성을 증가 시키는가?«였으며 제 대답은 '예'입니다. 이 경우 생략 할 수 있습니다.
토마스 정크

그리고 : 아니요! 나는 downvote를 이해하지 못한다.
토마스 정크

-1

짧은 이야기를 짧게하려면이 경우 else키워드를 제거 하는 것이 좋습니다. 여기에는 완전히 중복되어 있으며 코드를 포맷하는 방법에 따라 코드에 완전히 쓸모없는 1 ~ 3 줄 이 추가됩니다 . if블록에 무엇이 있는지 고려하십시오 .

return true;

따라서 if블록을 입력 해야하는 경우 나머지 기능 전체는 정의에 따라 실행되지 않습니다. 그러나 입력하지 않으면 더 이상 if명령문 이 없으므로 나머지 함수 전체가 실행됩니다. return성명서에 내용이 없으면 완전히 다를 수 있습니다 .if 블록에 의 존재 유무else 블록이 영향을 미칠 것이다, 그러나 여기,이 영향이 없을 것입니다.

귀하의 질문에, 귀하의 if상태를 반전 시키고 생략 else한 후에 다음을 진술했습니다.

이제 첫 번째 조건을 즉시 올바르게 인식하지 않고 첫 번째 예 이후의 조건을 기반으로 새 if 블록을 추가 할 수 있습니다.

그런 다음 코드를 조금 더 자세히 읽어야합니다. 우리는 이러한 문제를 완화하기 위해 고급 언어와 명확한 코드 구성을 사용하지만 완전히 중복 된 else블록을 추가하면 코드에 "노이즈"와 "클러 터"가 추가되므로 if조건 을 반전하거나 역전시킬 필요도 없습니다 . 그들이 차이점을 알 수 없다면, 그들이 무엇을하고 있는지 모릅니다. 그들이 차이점을 알 수 있다면 항상 원래의 if조건 자체를 뒤집을 수 있습니다 .

else 블록의 생략에 동의하는 경우를 언급하지 않았으며 if 블록을 사용하여 null-check (또는 다른 경비원)와 같은 모든 다음 코드에 대해 논리적으로 충족되어야하는 제약 조건을 적용하는 경우를 언급했습니다. ).

그것은 본질적으로 여기서 일어나는 일입니다. if블록 에서 돌아 왔 으므로 if평가 조건 false 은 다음과 같습니다. 다음 코드 모두에 대해 논리적으로 충족되어야하는 제약 조건입니다.

else 블록을 포함하지 않아서 복잡성이 증가합니까?

아니요, 이는 코드의 복잡성을 감소 시키며 특정 초소형 최적화가 발생할지 여부에 따라 컴파일 된 코드의 감소를 구성 할 수도 있습니다.


2
"그들은 코드를 조금 더 자세히 읽어야합니다." 코딩 스타일은 프로그래머가 이질적인 디테일에 대한 관심을 줄이고 실제로 수행하는 작업에 더 많은 관심을 기울일 수 있도록합니다. 스타일이 불필요하게 이러한 세부 사항에주의를 기울이면 나쁜 스타일입니다. "... 완전히 쓸모없는 라인 ..."그것들은 쓸모가 없습니다. 그들은이 부분이나 그 부분이 실행될 때 어떤 일이 일어날 지에 대한 시간을 낭비하지 않고도 구조가 무엇인지 한눈에 볼 수있게합니다.
Michael Shaw

@MichaelShaw Aviv Cohn의 답변이 내가 말하는 것에 들어가는 것 같습니다.
Panzercrisis 2016 년

-2

당신이 준 구체적인 예에서 그렇습니다.

  • 검토자가 코드를 다시 읽도록하여 오류가 아닌지 확인합니다.
  • 플로우 그래프에 하나의 노드를 추가하기 때문에 (필요하지 않은) 순환 복잡성을 추가합니다.

나는 이것이 더 간단하게 얻을 수 없다고 생각한다.

bool CanLeaveWithoutUmbrella()
{
    return (sky.Color == Color.Blue);
}

6
필자는이 매우 간단한 예제 를 다시 작성하여 if명령문 을 제거 할 수 있다는 사실을 구체적으로 설명합니다. 사실 나는 당신이 사용한 정확한 코드를 사용하여 더 단순화하지 않는 것을 명시 적으로 권장하지 않습니다. 또한 순환적인 복잡성은 else블록에 의해 증가되지 않습니다 .
Selali Adobor

-4

나는 항상 의도가 최대한 명확하도록 코드를 작성하려고합니다. 귀하의 예제에는 컨텍스트가 부족하므로 조금 수정하겠습니다.

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        if(this.color == Color.Blue)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

우리의 의도 는 하늘이 파란색 일 때 우산없이 떠날 수 . 그러나 코드의 바다에서는 그 단순한 의도가 사라집니다. 이해하기 쉽도록 몇 가지 방법이 있습니다.

먼저 else지점 에 대한 질문이 있습니다 . 어느 쪽도 신경 쓰지 않지만 else. 나는 명백한 것에 대한 주장을 이해하지만, 내 의견은 if진술이 '선택적인'지점을 감싸 야한다는 return true것입니다. 나는 return false우리의 기본 역할을하기 때문에 분기를 '선택적' 이라고 부르지 않을 것 입니다. 어느 쪽이든, 나는 이것이 매우 사소한 문제이며 다음 문제보다 훨씬 덜 중요하다고 생각합니다.

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        if(this.color == Color.Blue)
        {
            return true;
        }
        return false;
    }
}

훨씬 더 중요한 문제는 가지가 너무 많이하고있는 것입니다! 브랜치는 모든 것과 마찬가지로 '가능한 한 단순하지만 더 이상은 아니어야'합니다. 이 경우 if, 실제로 분기 할 필요가있을 때 표현식 (값) 인 코드 블록 (문 모음) 사이에서 분기 하는 명령문을 사용 했습니다 . a) 코드 블록에는 단일 명령문 만 포함되어 있으며 b) 동일한 명령문 ( ) 이므로 명확합니다 . 삼항 연산자 를 사용하여 분기를 다른 부분으로 좁힐 수 있습니다 .return?:

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        return (this.color == Color.Blue)
            ? true
            : false;
    }
}

이제 우리는 결과가 동일하다는 명백한 중복에 도달합니다 this.color == Color.Blue. 나는 당신의 질문이 우리에게 이것을 무시하도록 요구한다는 것을 알고 있지만, 이것이 매우 일차적이고 절차적인 사고 방식임을 지적하는 것이 정말로 중요하다고 생각합니다. 입력 값을 세분화하고 일부 출력 값을 구성하고 있습니다. 괜찮지 만 가능 하면 입력과 출력 간의 관계 또는 변환 측면에서 생각하는 것이 훨씬 강력합니다 . 이 경우 관계는 분명히 동일하다는 것이지만, 나는 종종 이와 같은 복잡한 절차 코드를 보는데, 이는 간단한 관계를 모호하게합니다. 계속해서 다음과 같이 단순화하십시오.

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool CanLeaveWithoutUmbrella()
    {
        return (this.color == Color.Blue);
    }
}

다음으로 지적해야 할 것은 API에 이중 음수가 포함되어 CanLeaveWithoutUmbrella있다는 것 false입니다. 이, 물론,에 단순화 NeedUmbrella존재는 true, 그래서 그렇게하자 :

class Sky {
    Sky(Colour c)
    {
        this.color = c;
    }
    bool NeedUmbrella()
    {
        return (this.color != Color.Blue);
    }
}

이제 코딩 스타일에 대해 생각할 수 있습니다. 객체 지향 언어를 사용하는 것처럼 보이지만 if명령문을 사용하여 코드 블록 사이를 분기하면 절차 를 작성하게 됩니다. 코드를 .

객체 지향 코드에서 모든 코드 블록은 메소드 이며 모든 분기는 동적 디스패치 를 통해 수행됩니다 . 클래스 또는 프로토 타입 사용. 그것이 더 간단하게 만들지 여부는 논쟁의 여지가 있지만 코드가 나머지 언어와 더 일관성이 있어야합니다.

class Sky {
    bool NeedUmbrella()
    {
        return true;
    }
}

class BlueSky extends Sky {
    bool NeedUmbrella()
    {
        return false;
    }
}

삼항 연산자를 사용하여 표현식을 분기하는 것은 함수형 프로그래밍 에서 일반적입니다 .


대답의 첫 번째 단락은 질문에 대답하는 데 궤도에있는 것처럼 보이지만 바로이 간단한 예제를 무시하고 명시 적으로 요구하는 정확한 주제로 즉시 분기됩니다 . 질문에서 : . 사실 당신은 내가 언급 한 정확한 코드 줄을 사용합니다. 또한 질문 의이 부분이 주제가 아닌 반면, 나는 당신이 추론에 너무 많이 가고 있다고 말하고 싶습니다. 당신은 한 근본적으로 코드가 무엇인지 변경되었습니다. I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Selali Adobor

@AssortedTrailmix 이것은 순수한 스타일의 문제입니다. 스타일에 관심을 갖는 것이 합리적이고 스타일을 무시하는 것이 합리적입니다 (결과에만 집중). 내가 걱정에 의미가 있다고 생각하지 않습니다 return false;else { return false; }동안 하지 당신이 OO 언어로 절차 적 코드를 작성하는 경우, 근본적인 변화가 필요하다 등 지점 극성, 동적 파견, ternaries에 대한 배려)
Warbo

일반적으로 이와 같은 것을 말할 수 있지만 잘 정의 된 매개 변수 로 질문에 대답 할 때는 적용 할 수 없습니다 (특히 이러한 매개 변수를 고려하지 않으면 전체 질문을 "추출"할 수 있음). 또한 마지막 문장은 명백한 잘못이라고 말하고 싶습니다. OOP는 "절차 소멸"이 아니라 "절차 추상화"에 관한 것입니다. 나는 당신이 하나의 라이너에서 무한한 수 의 클래스 (각 색상마다 하나씩)로 갔다는 사실이 그 이유를 보여줍니다. 또한 나는 여전히 동적 디스패치가 if/else명령문과 어떤 관련이 있는지, 그리고 분기 극성이 무엇인지 궁금합니다 .
Selali Adobor

나는 OO 솔루션이 더 좋다고 말한 적이 없으며 (사실 함수형 프로그래밍을 선호합니다), 언어와 더 일관성이 있다고 말했습니다. "극성"이란 어떤 것이 "참"이고 어떤 것이 "거짓"인지를 의미합니다 ( "NeedUmbrella"로 전환). OO 설계에서 모든 제어 흐름은 동적 디스패치에 의해 결정됩니다. 예를 들어, Smalltalk는 en.wikipedia.org/wiki/Smalltalk#Control_structures를 허용하지 않습니다 ( c2.com/cgi/wiki?HeInventedTheTerm 참조 )
Warbo

2
나는 마지막 단락 (약 OO)에 도달 할 때까지 이것을 upvoted했을 것입니다. 그것은 정확히 라비올리 코드 를 생성하는 OO 구문의 일종의 생각하지 않은 과도한 사용입니다 . 이것은 스파게티만큼 읽을 수 없습니다.
zwol
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.