태그 보관물: coding-style

coding-style

다른 경우를 처리하는 우아한 방법 문제이지만, 이런 식으로 코딩해야 할

이것은 사소한 문제이지만, 이런 식으로 코딩해야 할 때마다 반복이 귀찮게하지만 솔루션 중 어느 것이 나쁘지 않은지 확실하지 않습니다.

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}
  • 이런 종류의 논리에 대한 이름이 있습니까?
  • 나는 너무 OCD인가?

호기심을 위해서만 악의적 인 코드 제안에 개방적입니다 …



답변

함수 (방법)를 분리하고 return명령문을 사용하도록 추출하십시오 .

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        return;
    }
}

DefaultAction();

또는 컨텐츠와 처리를 분리하여 분리하는 것이 좋습니다.

contents_t get_contents(name_t file)
{
    if(!FileExists(file))
        return null;

    contents = OpenFile(file);
    if(!SomeTest(contents)) // like IsContentsValid
        return null;

    return contents;
}

...

contents = get_contents(file)
contents ? DoSomething(contents) : DefaultAction();

Upd :

예외 OpenFile가 아닌 이유, IO 예외가 발생하지 않는 이유 :
파일 IO에 대한 질문이 아니라 실제로 일반적인 질문이라고 생각합니다. 같은 이름은 FileExists, OpenFile복잡 할 수 있지만, 그들을 대체 할 경우 Foo, Bar등, -이 명확 할 것 DefaultAction같은 자주 호출 할 수 있습니다 DoSomething이 아닌 예외적 인 경우가 될 수 있도록. Péter Török은 답변끝날 때 이에 대해 썼습니다

두 번째 변형에 삼항 조건 연산자가있는 이유 :
[C ++] 태그가 있으면 조건 부분에 if선언이 contents있는 명령문을 작성 했습니다.

if(contents_t contents = get_contents(file))
    DoSomething(contents);
else
    DefaultAction();

그러나 다른 (C와 같은) 언어의 경우 if(contents) ...; else ...;삼항 조건 연산자가있는 표현식 문과 정확히 동일하지만 더 길다. 코드의 주요 부분은 get_contents함수 였기 때문에 더 짧은 버전을 사용했습니다 (그리고 생략 된 contents유형). 어쨌든, 그것은이 질문을 넘어선 것입니다.


답변

사용하는 프로그래밍 언어가 (0) 단락 이진 비교 (즉, false를 반환 SomeTest하면 호출하지 않는 경우 FileExists) 및 (1) 할당이 값을 반환하면 (결과 OpenFile가 할당 된 contents다음 해당 값이 인수로 전달됨) to SomeTest), 다음과 같은 것을 사용할 수는 있지만 여전히 단일 코드 =가 의도적 이라는 점을 지적하면서 코드에 의견을 제시하는 것이 좋습니다 .

if( FileExists(file) && SomeTest(contents = OpenFile(file)) )
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}

if의 복잡성에 따라 플래그 변수 ( DefaultAction이 경우 오류를 처리하는 코드로 성공 / 실패 조건 테스트를 분리)를 사용하는 것이 좋습니다.


답변

DefaultAction에 대한 호출을 반복하는 것보다 더 심각한 것은 코드가 직교하지 않기 때문에 스타일 자체입니다 ( 직교 적으로 작성해야하는 좋은 이유는 이 답변 참조 ).

비 직교 코드가 나쁜 이유를 보여주기 위해 네트워크 디스크에 저장된 경우 파일을 열지 않아야하는 새로운 요구 사항이 소개 될 때 원래 예를 고려하십시오. 그러면 코드를 다음과 같이 업데이트 할 수 있습니다.

if(FileExists(file))
{
    if(! OnNetworkDisk(file))
    {
        contents = OpenFile(file); // <-- prevents inclusion in if
        if(SomeTest(contents))
        {
            DoSomething(contents);
        }
        else
        {
            DefaultAction();
        }
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

그러나 2Gb 이상으로 큰 파일을 열지 않아야한다는 요구 사항도 있습니다. 글쎄, 우리는 다시 업데이트 :

if(FileExists(file))
{
    if(LessThan2Gb(file))
    {
        if(! OnNetworkDisk(file))
        {
            contents = OpenFile(file); // <-- prevents inclusion in if
            if(SomeTest(contents))
            {
                DoSomething(contents);
            }
            else
            {
                DefaultAction();
            }
        }
        else
        {
            DefaultAction();
        }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

이러한 코드 스타일은 유지 관리에 큰 어려움이 될 것입니다.

여기에 올바르게 직교로 작성된 답변 중 Abyx의 두 번째 예Jan Hudec의 답변 이 반복되지 않으므로 해당 답변에 두 가지 요구 사항을 추가하면

if(! LessThan2Gb(file))
    return null;

if(OnNetworkDisk(file))
    return null;

(또는 goto notexists;대신 return null;) 추가 된 행 이외의 다른 코드에는 영향을 미치지 않습니다 . 예를 들어 직교.

테스트 할 때 일반적인 규칙은 일반적인 경우가 아니라 예외테스트 해야합니다 .


답변

명백하게:

Whatever(Arguments)
{
    if(!FileExists(file))
        goto notexists;
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(!SomeTest(contents))
        goto notexists;
    DoSomething(contents);
    return;
notexists:
    DefaultAction();
}

당신은 당신이 악한 해결책에도 열려 있다고 말 했으니 악한 고토를 사용하십시오.

실제로 상황에 따라이 솔루션은 악의적 인 행동을 두 번하거나 악의적 인 추가 변수보다 덜 악할 수 있습니다. 긴 함수의 중간에 (적어도 중간에 리턴으로 인해) 확실하지 않기 때문에 함수에 래핑했습니다. 그러나 긴 기능보다 OK, 기간은 아닙니다.

예외가있을 때, 특히 OpenFile 및 DoSomething이 조건이 충족되지 않으면 예외를 던질 수 있으므로 예외를 쉽게 읽을 수 있으므로 명시적인 검사가 필요하지 않습니다. 반면에 C ++에서 Java 및 C #에서 예외를 발생시키는 작업은 느리게 수행되므로 성능면에서 goto가 여전히 바람직합니다.


“악”에 대한 참고 사항 : C ++ FAQ 6.15 는 “악”을 다음과 같이 정의합니다.

그것은 그런 것을 의미 하며 그런 것은 대부분 피해야 하는 것이지만 항상 피해야 하는 것은 아닙니다 . 예를 들어, “악한 대안 중에서 가장 악한 것이 가장 적을 때마다” “악한”것을 사용하게됩니다.

그리고 그것은 goto이 맥락에서 적용됩니다 . 구조화 된 흐름 제어 구조는 대부분 더 낫지 만 조건에 할당하거나 약 3 레벨 이상 깊이 중첩, 코드 중복 또는 긴 조건과 같이 너무 많은 자체 악을 축적하는 상황에 처하면 goto단순히 끝날 수 있습니다 덜 악한 것.


답변

function FileContentsExists(file) {
    return FileExists(file) ? OpenFile(file) : null;
}

contents = FileContentExists(file);
if(contents && SomeTest(contents))
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}

답변

한 가지 가능성 :

boolean handled = false;

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        handled = true;
    }
}
if (!handled)
{
    DefaultAction();
}

물론 이것은 다른 방식으로 코드를 약간 더 복잡하게 만듭니다. 스타일 문제입니다.

다른 접근법은 예외를 사용하는 것입니다. 예 :

try
{
    contents = OpenFile(file); // throws IO exception if file not found
    DoSomething(contents); // calls SomeTest() and throws exception on failure
}
catch(Exception e)
{
    DefaultAction();
    // and the exception should be at least logged...
}

이것은 더 단순 해 보이지만 다음 경우에만 해당됩니다.

  • 우리는 어떤 종류의 예외를 예상 DefaultAction()하고 각각에 맞는지 정확하게 알고 있습니다.
  • 파일 처리가 성공할 것으로 예상되며 누락 된 파일 또는 실패 SomeTest()는 분명히 잘못된 조건이므로 예외를 처리하는 것이 적합합니다.

답변

이것은 더 높은 수준의 추상화입니다.

if (WeCanDoSomething(file))
{
   DoSomething(contents);
}
else
{
   DefaultAction();
} 

그리고 이것은 세부 사항을 채 웁니다.

boolean WeCanDoSomething(file)
{
    if FileExists(file)
    {
        contents = OpenFile(file);
        return (SomeTest(contents));
    }
    else
    {
        return FALSE;
    }
}