program story

'if'문이 왜 악한 것으로 간주됩니까?

inputbox 2020. 11. 22. 19:27
반응형

'if'문이 왜 악한 것으로 간주됩니까?


저는 Simple Design and Testing Conference 에서 왔습니다 . 세션 중 하나에서 우리는 프로그래밍 언어의 사악한 키워드에 대해 이야기했습니다. 주제를 제안한 Corey Hainesif진술이 절대적인 악 이라고 확신했습니다 . 그의 대안은 술어로 함수를 만드는 것이 었습니다 . if악한 지 설명해 주시겠습니까?

나는 당신이 매우 추악한 코드를 남용 할 수 있다는 것을 이해합니다 if. 그러나 나는 그것이 그렇게 나쁘다고 생각하지 않습니다.


조건절은 때때로 관리하기 어려운 코드를 생성합니다. 여기에는 if진술뿐만 아니라 일반적으로 switch해당하는 if보다 더 많은 분기를 포함 하는 진술이 포함됩니다 .

사용하는 것이 완벽하게 합리적인 경우가 있습니다. if

유틸리티 메서드, 확장 또는 특정 라이브러리 함수를 작성할 때 ifs 를 피할 수 없을 가능성이 높습니다 (그렇게해서는 안됩니다). 이 작은 함수를 코딩하는 더 좋은 방법은 없으며 현재보다 더 자체 문서화 할 수있는 방법은 없습니다.

// this is a good "if" use-case
int Min(int a, int b)
{
    if (a < b) 
       return a;
    else
       return b;
}

// or, if you prefer the ternary operator
int Min(int a, int b)
{
    return (a < b) ? a : b;
}

"유형 코드"를 통해 분기하는 것은 코드 냄새입니다.

반면에 일종의 유형 코드를 테스트하거나 변수가 특정 유형인지 테스트하는 코드를 발견하면 리팩토링, 즉 조건부를 다형성으로 대체 하기에 좋은 후보 일 가능성이 큽니다 .

그 이유는 호출자가 특정 유형 코드에서 분기 할 수 있도록 허용함으로써 코드 전체에 흩어져있는 수많은 검사로 끝날 가능성을 만들어 확장 및 유지 관리를 훨씬 더 복잡하게 만들기 때문입니다. 반면에 다형성을 사용하면 이러한 분기 결정을 프로그램의 루트에 최대한 가깝게 가져올 수 있습니다.

중히 여기다:

// this is called branching on a "type code",
// and screams for refactoring
void RunVehicle(Vehicle vehicle)
{
    // how the hell do I even test this?
    if (vehicle.Type == CAR)
        Drive(vehicle);
    else if (vehicle.Type == PLANE)
        Fly(vehicle);
    else
        Sail(vehicle);
}

공통이지만 유형별 (즉, 클래스 별) 기능을 별도의 클래스에 배치하고이를 가상 메서드 (또는 인터페이스)를 통해 노출함으로써 프로그램의 내부 부분에서이 결정을 호출 계층의 상위에 위임 할 수 있습니다 ( 잠재적으로 코드의 단일 위치), 훨씬 더 쉬운 테스트 (모킹), 확장 성 및 유지 관리가 가능합니다.

// adding a new vehicle is gonna be a piece of cake
interface IVehicle
{
    void Run();
}

// your method now doesn't care about which vehicle 
// it got as a parameter
void RunVehicle(IVehicle vehicle)
{
    vehicle.Run();
}

이제 RunVehicle메서드가 정상적으로 작동하는지 쉽게 테스트 할 수 있습니다 .

// you can now create test (mock) implementations
// since you're passing it as an interface
var mock = new Mock<IVehicle>();

// run the client method
something.RunVehicle(mock.Object);

// check if Run() was invoked
mock.Verify(m => m.Run(), Times.Once());

if조건 만 다른 패턴 은 재사용 가능

if귀하의 질문에서 "술어"로 대체하는 것에 대한 주장과 관련하여 Haines는 조건식에서만 다른 유사한 패턴이 귀하의 코드에 존재한다는 것을 언급하고 싶었을 것입니다. 조건식은 ifs 와 함께 등장 하지만 전체 아이디어는 반복 패턴을 별도의 메서드로 추출하여 표현식을 매개 변수로 남겨 두는 것입니다. 이것은 LINQ가 이미 수행 한 작업이며 일반적으로 대체 코드에 비해 코드가 더 깔끔합니다 foreach.

다음 두 가지 매우 유사한 방법을 고려하십시오.

// average male age
public double AverageMaleAge(List<Person> people)
{
    double sum = 0.0;
    int count = 0;
    foreach (var person in people)
    {
       if (person.Gender == Gender.Male)
       {
           sum += person.Age;
           count++;
       }
    }
    return sum / count; // not checking for zero div. for simplicity
}

// average female age
public double AverageFemaleAge(List<Person> people)
{
    double sum = 0.0;
    int count = 0;
    foreach (var person in people)
    {
       if (person.Gender == Gender.Female) // <-- only the expression
       {                                   //     is different
           sum += person.Age;
           count++;
       }
    }
    return sum / count;
}

이는 조건을 조건 자로 추출 할 수 있음을 나타내며 다음 두 가지 경우 (및 다른 많은 향후 사례)에 대한 단일 방법을 사용할 수 있습니다.

// average age for all people matched by the predicate
public double AverageAge(List<Person> people, Predicate<Person> match)
{
    double sum = 0.0;
    int count = 0;
    foreach (var person in people)
    {
       if (match(person))       // <-- the decision to match
       {                        //     is now delegated to callers
           sum += person.Age;
           count++;
       }
    }
    return sum / count;
}

var males = AverageAge(people, p => p.Gender == Gender.Male);
var females = AverageAge(people, p => p.Gender == Gender.Female);

LINQ에는 이미 이와 같은 편리한 확장 메서드가 많이 있으므로 실제로 메서드를 직접 작성할 필요도 없습니다.

// replace everything we've written above with these two lines
var males = list.Where(p => p.Gender == Gender.Male).Average(p => p.Age);
var females = list.Where(p => p.Gender == Gender.Female).Average(p => p.Age);

이 마지막 LINQ 버전에서는 다음과 같은 경우에도 if문이 완전히 "사라졌습니다".

  1. 솔직히 말해서 문제는 if그 자체가 아니라 전체 코드 패턴에서 발생했습니다 (단순히 복제 되었기 때문에).
  2. if아직 실제로 존재하지만, 그것은 LINQ의 내부에 쓰여 Where테스트와 수정을 위해 폐쇄 된 확장 방법. 자신의 코드를 적게 사용하는 것은 항상 좋은 것입니다. 테스트 할 일이 적고 잘못 될 일이 적으며 코드를 따르고, 분석하고, 유지하기가 더 간단합니다.

코드 냄새라고 느낄 때 리팩터링하지만 과도하게 엔지니어링하지 마십시오.

이 모든 것을 말했듯이, 당신 지금 여기저기서 몇 가지 조건문을 갖는 것보다 잠 못 이루는 밤을 보내서는 안됩니다 . 이러한 답변은 일반적인 경험 규칙을 제공 할 수 있지만 리팩토링이 필요한 구조를 감지 할 수있는 가장 좋은 방법은 경험을 통한 것입니다. 시간이 지남에 따라 동일한 절을 반복해서 수정하는 일부 패턴이 나타납니다.


if악할 수있는 또 다른 감각이 있습니다. 다형성 대신에 올 때입니다.

 if (animal.isFrog()) croak(animal)
 else if (animal.isDog()) bark(animal)
 else if (animal.isLion()) roar(animal)

대신에

 animal.emitSound()

그러나 기본적으로 그것이하는 일에 대해 완벽하게 수용 가능한 도구라면. 물론 남용되거나 오용 될 수 있지만 고토의 지위에 가까운 곳은 없습니다.


Code Complete의 좋은 인용문 :

당신의 프로그램을 관리하는 사람이 당신이 사는 곳을 아는 폭력적인 사이코 패스 인 것처럼 코딩하세요.
— 익명

IOW, 간단하게 유지하십시오. 특정 영역에서 술어를 사용하여 애플리케이션의 가독성을 높이려면이를 사용하십시오. 그렇지 않으면 'if'를 사용하고 계속 진행하십시오.


솔직히 무엇을하는지에 달려 있다고 생각합니다.

간단한 if..else진술 이 있다면 predicate?

가능하면 switchif대체를 위해 a를 사용하고 대규모 작업에 술어를 사용하는 옵션이 있으면 사용하십시오. 그렇지 않으면 코드를 유지 관리하는 데 악몽이 될 것입니다.

이 녀석은 내 취향에 대해 약간 현학적 인 것 같습니다. 모든 if의를 술어로 대체하는 것은 미친 이야기입니다.


있습니다 안티 - 만약에 올해 초에 시작된 캠페인. 주요 전제는 많은 중첩 if 문이 종종 다형성으로 대체 될 수 있다는 것입니다.

대신 Predicate를 사용하는 예를보고 싶습니다. 이것이 함수형 프로그래밍 라인에 더 가깝습니까?


if (나는 또한 코드 작성 관행에 도덕성을 부여하는 것이 무의미하다고 생각합니다 ...).

헤인즈 씨는 어리 석고 웃어야합니다.


돈에 관한 성경 구절처럼 만약 진술이 악하지 않다면-if 진술의 사랑은 악합니다. if 문이없는 프로그램은 우스꽝스러운 생각이며 필요에 따라 사용하는 것이 필수적입니다. 그러나 연속적으로 100 개의 if-else if 블록을 가진 프로그램 (슬프게도 내가 본 것)은 확실히 악합니다.


나는 최근에 if 문을 코드 냄새로보기 시작했다고 말해야한다. 특히 같은 조건을 여러 번 반복하는 자신을 발견 할 때. 하지만 코드 냄새에 대해 이해해야 할 것이 있습니다. 코드 냄새가 반드시 코드가 나쁘다는 것을 의미하지는 않습니다. 그들은 단지 코드가 나쁘다는 좋은 기회 가 있다는 것을 의미합니다 .

예를 들어, 주석은 Martin Fowler에 의해 코드 냄새로 나열되지만 "댓글은 악의적입니다. 사용하지 마십시오"라고 말하는 사람은 심각하게 받아들이지 않습니다.

일반적으로 가능한 경우 if 문 대신 다형성을 사용하는 것을 선호합니다. 따라서 오류의 여지가 훨씬 적습니다. 조건문을 사용하면 많은 시간 동안 많은 부정확 한 인수가 발생한다는 것을 발견하는 경향이 있습니다 (조건부를 구성하는 데 필요한 데이터를 적절한 메서드에 전달해야하기 때문입니다).


동의합니다. 그는 틀렸다 . 그런 일로 너무 멀리 갈 수 있고 자신의 이익을 위해 너무 영리 할 수 ​​있습니다.

ifs 대신 술어로 작성된 코드는 유지 보수 및 테스트하기가 끔찍합니다.


Predicates come from logical/declarative programming languages, like PROLOG. For certain classes of problems, like constraint solving, they are arguably superior to a lot of drawn out step-by-step if-this-do-that-then-do-this crap. Problems that would be long and complex to solve in imperative languages can be done in just a few lines in PROLOG.

There's also the issue of scalable programming (due to the move towards multicore, the web, etc.). If statements and imperative programming in general tend to be in step-by-step order, and not scaleable. Logical declarations and lambda calculus though, describe how a problem can be solved, and what pieces it can be broken down into. As a result, the interpreter/processor executing that code can efficiently break the code into pieces, and distribute it across multiple CPUs/cores/threads/servers.

Definitely not useful everywhere; I'd hate to try writing a device driver with predicates instead of if statements. But yes, I think the main point is probably sound, and worth at least getting familiar with, if not using all the time.


Perhaps with quantum computing it will be a sensible strategy to not use IF statements but to let each leg of the computation proceed and only have the function 'collapse' at termination to a useful result.


The only problem with a predicates (in terms of replacing if statements) is that you still need to test them:

function void Test(Predicate<int> pr, int num) 
{
    if (pr(num))
    { /* do something */ }
    else
    { /* do something else */ }
}

You could of course use the terniary operator (?:), but that's just an if statement in disguise...


It probably comes down to a desire to keep code cyclomatic complexity down, and to reduce the number of branch points in a function. If a function is simple to decompose into a number of smaller functions, each of which can be tested, you can reduce the complexity and make code more easily testable.


IMO: I suspect he was trying to provoke a debate and make people think about the misuse of 'if'. No one would seriously suggest such a fundamental construction of programming syntax was to be completely avoided would they?


Sometimes it's necessary to take an extreme position to make your point. I'm sure this person uses if -- but every time you use an if, it's worth having a little think about whether a different pattern would make the code clearer.

Preferring polymorphism to if is at the core of this. Rather than:

if(animaltype = bird) {
     squawk();
} else if(animaltype = dog) {
     bark();
}

... use:

animal.makeSound();

But that supposes that you've got an Animal class/interface -- so really what the if is telling you, is that you need to create that interface.

So in the real world, what sort of ifs do we see that lead us to a polymorphism solution?

if(logging) {
      log.write("Did something");
}

That's really irritating to see throughout your code. How about, instead, having two (or more) implementations of Logger?

this.logger = new NullLogger();    // logger.log() does nothing
this.logger = new StdOutLogger();  // logger.log() writes to stdout

That leads us to the Strategy Pattern.

Instead of:

if(user.getCreditRisk() > 50) {
     decision = thoroughCreditCheck();
} else if(user.getCreditRisk() > 20) {
     decision = mediumCreditCheck();
} else {
     decision = cursoryCreditCheck();
}

... you could have ...

decision = getCreditCheckStrategy(user.getCreditRisk()).decide();

Of course getCreditCheckStrategy() might contain an if -- and that might well be appropriate. You've pushed it into a neat place where it belongs.


I think If statements are evil, but If expressions are not. What I mean by an if expression in this case can be something like the C# ternary operator (condition ? trueExpression : falseExpression). This is not evil because it is a pure function (in a mathematical sense). It evaluates to a new value, but it has no effects on anything else. Because of this, it works in a substitution model.

Imperative If statements are evil because they force you to create side-effects when you don't need to. For an If statement to be meaningful, you have to produce different "effects" depending on the condition expression. These effects can be things like IO, graphic rendering or database transactions, which change things outside of the program. Or, it could be assignment statements that mutate the state of the existing variables. It is usually better to minimize these effects and separate them from the actual logic. But, because of the If statements, we can freely add these "conditionally executed effects" everywhere in the code. I think that's bad.


Good that in ruby we have unless ;)

But seriously probably if is the next goto, that even if most of the people think it is evil in some cases is simplifying/speeding up the things (and in some cases like low level highly optimized code it's a must).


If is not evil! Consider ...

int sum(int a, int b) {
    return a + b;
}

Boring, eh? Now with an added if ...

int sum(int a, int b) {
    if (a == 0 && b == 0) {
        return 0;
    }
    return a + b;
}

... your code creation productivity (measured in LOC) is doubled.

Also code readability has improved much, for now you can see in the blink of an eye what the result is when both argument are zero. You couldn't do that in the code above, could you?

Moreover you supported the testteam for they now can push their code coverage test tools use up more to the limits.

Furthermore the code now is better prepared for future enhancements. Let's guess, for example, the sum should be zero if one of the arguments is zero (don't laugh and don't blame me, silly customer requirements, you know, and the customer is always right). Because of the if in the first place only a slight code change is needed.

int sum(int a, int b) {
    if (a == 0 || b == 0) {
        return 0;
    }
    return a + b;
}

How much more code change would have been needed if you hadn't invented the if right from the start.

Thankfulness will be yours on all sides.

Conclusion: There's never enough if's.

There you go. To.

참고URL : https://stackoverflow.com/questions/1554180/why-is-the-if-statement-considered-evil

반응형