为什么'if'陈述被认为是邪恶的? [英] Why is the 'if' statement considered evil?

查看:161
本文介绍了为什么'if'陈述被认为是邪恶的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚来自简单设计和测试会议。在其中一个会话中,我们讨论的是编程语言中的邪恶关键词。建议该主题的 Corey Haines 确信如果陈述绝对是邪恶的。他的另一种方法是使用谓词创建函数。你可以向我解释为什么如果是邪恶的。

I just came from Simple Design and Testing Conference. In one of the session we were talking about evil keywords in programming languages. Corey Haines, who proposed the subject, was convinced that if statement is absolute evil. His alternative was to create functions with predicates. Can you please explain to me why if is evil.

我知道你可以编写非常难看的代码滥用如果。但我不相信它那么糟糕。

I understand that you can write very ugly code abusing if. But I don't believe that it's that bad.

推荐答案

条件子句有时会导致代码难以管理。这不仅包括 if 语句,更常见的是开关语句,它通常包含比相应的更多分支如果会。

Conditional clauses sometimes do result in code which is harder to manage. This not only includes the if statement, but even more commonly the switch statement, which usually includes more branches than a corresponding if would.

当您编写实用程序方法,扩展或特定库函数时,您可能无法避免如果 s(你不应该)。没有更好的方法来编写这个小函数,也没有比它更自我记录:

When you are writing utility methods, extensions or specific library functions, it's likely that you won't be able to avoid ifs (and you shouldn't). There isn't a better way to code this little function, nor make it more self-documented than it is:

// 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;
}



对类型代码进行分支是代码气味



另一方面,如果遇到代码测试某种类型的代码,或者测试变量是否属于某种类型,那么这很可能是重构的一个很好的候选者,即用多态性替换条件

这样做的原因是,通过允许您的呼叫者分支某个类型代码,您可能会在代码中散布大量支票,从而使扩展和维护变得更加复杂。另一方面,多态性允许您将此分支决策尽可能地靠近程序的根目录。

The reason for this is that by allowing your callers to branch on a certain type code, you are creating a possibility to end up with numerous checks scattered all over your code, making extensions and maintenance much more complex. Polymorphism on the other hand allows you to bring this branching decision as closer to the root of your program as possible.

考虑:

// 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);
}

通过将常见但特定于类型(即特定于类)的功能放入单独的类和通过虚拟方法(或接口)公开它,您允许程序的内部部分将此决策委派给调用层次结构中较高的人(可能在代码中的单个位置),从而允许更容易的测试(模拟) ,可扩展性和维护:

By placing common but type-specific (i.e. class-specific) functionality into separate classes and exposing it through a virtual method (or an interface), you allow the internal parts of your program to delegate this decision to someone higher in the call hierarchy (potentially at a single place in code), allowing much easier testing (mocking), extensibility and maintenance:

// 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 方法可以正常工作:

And you can now easily test if your RunVehicle method works as it should:

// 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 的论点,Haines可能想要请注意,有时您的代码中存在类似的模式,这些模式仅在条件表达式上有所不同。条件表达式与一起出现,如果 s,但整个想法是将重复模式提取到单独的方法中,将表达式作为参数。这是LINQ已经做的,通常与替代 foreach相比产生更清晰的代码

Patterns which only differ in their if conditions can be reused

Regarding the argument about replacing if with a "predicate" in your question, Haines probably wanted to mention that sometimes similar patterns exist over your code, which differ only in their conditional expressions. Conditional expressions do emerge in conjunction with ifs, but the whole idea is to extract a repeating pattern into a separate method, leaving the expression as a parameter. This is what LINQ already does, usually resulting in cleaner code compared to an alternative foreach:

考虑以下两种非常相似的方法:

Consider these two very similar methods:

// average male age
public double AverageMaleAge(List<Person> people)
{
    double sum = 0.0;
    foreach (var person in people)
    {
       if (person.Gender == Gender.Male)
           sum += person.Age;
    }
    return sum / people.Count;
}

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

这表明您可以将条件提取到谓词中,这两种情况的单一方法(以及许多其他未来情况):

This indicates that you can extract the condition into a predicate, leaving you with a single method for these two cases (and many other future cases):

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

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

由于LINQ已经有一堆方便的扩展方法,你实际上根本不需要编写自己的方法:

And since LINQ already has a bunch of handy extension methods like this, you actually don't even need to write your own methods:

// 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 语句完全消失了,但是:

In this last LINQ version the if statement has "disappeared" completely, although:


  1. 说实话,问题不在本身,但在整个代码模式中(仅仅因为它是重复的),并且

  2. 如果仍然实际上存在,但它写在LINQ Where 扩展方法中,该方法已经过测试并已关闭以进行修改。拥有较少的自己的代码总是一件好事:要测试的东西少,出错的东西少,代码更容易遵循,分析和维护。

  1. to be honest the problem wasn't in the if by itself, but in the entire code pattern (simply because it was duplicated), and
  2. the if still actually exists, but it's written inside the LINQ Where extension method, which has been tested and closed for modification. Having less of your own code is always a good thing: less things to test, less things to go wrong, and the code is simpler to follow, analyze and maintain.



当你觉得它有代码味道时重构,但不要过度工程



说完这一切之后,你不应该度过不眠之夜而不是现在和那里有几个条件。虽然这些答案可以提供一些通用的经验法则,但能够检测需要重构的构造的最佳方法是通过经验。随着时间的推移,出现了一些模式,导致一遍又一遍地修改相同的条款。

Refactor when you feel it's a code smell, but don't over-engineer

Having said all this, you should not spend sleepless nights over having a couple of conditionals now and there. While these answers can provide some general rules of thumb, the best way to be able to detect constructs which need refactoring is through experience. Over time, some patterns emerge that result in modifying the same clauses over and over again.

这篇关于为什么'if'陈述被认为是邪恶的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆