为什么是“如果”声明认为是邪恶的?(Why is the 'if' statemen

2019-07-22 06:44发布

我刚从来简单的设计和测试会议 。 在会议中的一个,我们都在谈论编程语言邪恶的关键字。 科瑞海恩斯 ,谁提出的问题,相信, if声明是绝对的邪恶。 他的选择是创建具有功能的断言 。 为什么我能否请你解释一下if是邪恶的。

据我所知,你可以写非常丑陋的代码滥用if 。 但我不认为它是坏的。

Answer 1:

有条件的条款有时候会导致代码是难以管理。 这不仅包括if语句,但更常见的switch语句,它通常包括比相应的更多的分支机构if会。

在有些情况下这是完全合理使用的情况下if

当你写的实用方法,扩展或特定的库函数,很可能是你将无法避免if S(和你不应该)。 有没有更好的方式来编写这个小功能,也使其更自我记录比它是:

// 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在你的问题中“上游”,海恩斯可能想提一提,有时在你的代码,区别仅仅在于他们的条件表达式存在类似的模式。 条件表达式不结合出现与if S,但整个想法是提取的重复图案到一个单独的方法,留下表达作为参数。 这就是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扩展方法,它已经过测试,对修改关闭。 有少你自己的代码始终是一件好事:少的东西来测试,少的东西出问题,代码更简单跟随,分析和维护。

重构当你觉得这是一个代码味道,但不要过分工程师

说了这么多,你不应该花不眠之夜结束了,有有几个条件句。 虽然这些答案都可以提供一些经验的一般规则,最好的办法是能够检测这就需要重构是通过经验结构。 随着时间的推移,一些模式出现这一结果在一遍又一遍的修改同一条款。



Answer 2:

还有另外一个在意义上if可以避邪:说到代替多态性。

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

代替

 animal.emitSound()

但基本上,如果是做什么的完全可以接受的工具。 它可以被滥用,误用,当然,但它远不转到状态。



Answer 3:

从代码完成好报价:

代码仿佛负责维护你的程序是一个暴力变态谁知道你住在哪里。
-匿名

督察,保持简单。 如果您的应用程序的可读性会通过在特定区域使用谓词来增强,使用它。 否则,使用“如果”,继续前进。



Answer 4:

我认为这取决于你在做什么是诚实的。

如果你有一个简单if..else语句,为什么要用一个predicate

如果可以,用一个switch较大if更换,然后如果选择使用大型操作的谓词(它是有道理的,否则你的代码将保持梦魇),使用它。

这家伙似乎是我喜欢有点迂腐。 更换所有if “s的谓词仅仅是疯话。



Answer 5:

还有就是抗如果它在今年早些时候开始活动。 主要前提被很多嵌套if语句通常往往可以用多态性取代。

我很想看到一个使用谓词,而不是一个例子。 这是更沿函数式编程的线路?



Answer 6:

if不是恶(我也认为,为了代码编写规范分配道德是愚蠢的......)。

海恩斯先生是愚蠢的,应该被嘲笑。



Answer 7:

就像在关于金钱的经文,if语句不是邪恶 - if语句是罪恶的爱。 没有if语句是一个荒谬的想法,并利用它们作为必要的程序是必不可少的。 但是,有100程序的if-else如果在一个行(其中,可悲的是,我所看到的)块肯定是邪恶的。



Answer 8:

我不得不说,我最近已经开始查看if语句作为代码气味:特别是当你发现自己重复同样的条件下几次。 但有你需要了解一些代码味道:他们并不一定意味着该代码是坏的。 他们只是说,有一个很好的机会的代码是坏的。

例如,意见被列为由Martin Fowler一个代码味道,但我不会采取任何认真说谁“的评论是恶;不使用它们”。

通常而言,我更喜欢使用多态的,而不是if语句在可能的情况。 这只是让为错误少了这么多房间。 我往往会发现,很多的时候,使用条件语句导致大量的流浪汉参数,以及(因为你要通过形成到合适的方法条件所需的数据)。



Answer 9:

我同意你的看法; 他错了 。 你可以去用这样的事情太远,太聪明为自己的好。

与谓词代替IFS创建的代码将是可怕的维护和测试。



Answer 10:

谓词来自逻辑/声明性编程语言,如PROLOG。 对于某些类别的问题,如约束求解,他们可以说是优于很多抽出一步一步,如果-这-DO是-THEN-DO-此废话。 这将是长期和复杂的命令式语言来解决问题可以在短短的几行字在序言完成。

还有可扩展的编程问题(由于对多核移动,网页等)。 如果语句和一般的命令式编程往往是在一步一步顺序,并没有可扩展性。 逻辑声明和演算虽然,描述了一个问题如何解决,什么作品可以分解成。 其结果是,解释器/处理器执行该代码可以有效的代码分解成片,并且在多个CPU /核心/线程/服务器分发。

绝对不是到处有用; 我讨厌尝试写一个设备驱动程序与谓词来代替if语句。 但是,是的,我认为主要的一点是可能的声音,至少价值越来越熟悉,如果不使用所有的时间。



Answer 11:

也许与量子计算将是IF语句,但让计算每条腿进行,不使用,只有具备的功能“崩溃”在终止一个有用的结果,一个明智的策略。



Answer 12:

与谓词(在替代方面唯一的问题if语句)是,你仍然需要对其进行测试:

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

当然,你可以使用terniary运算符( ?: ),不过这只是一个if变相声明...



Answer 13:

这可能归结为保持代码的圈复杂度降低,并减少函数的分支点的数目的愿望。 如果一个函数是简单分解成多个小功能,每一种都可以进行测试,可以减少复杂性,使代码更容易测试。



Answer 14:

IMO:我怀疑他是想挑起辩论,让人们思考“如果”的滥用。 没有人会认真地建议编程语法的这样一个基本的结构是完全避免他们会吗?



Answer 15:

有时,有必要采取极端的立场,使你的观点。 我敢肯定,这个人使用if -但你每次都使用一个if ,这是值得拥有的一点点思考一个不同的模式是否会使代码更清晰。

宁愿多态if是在这个核心。 而不是:

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

... 使用:

animal.makeSound();

但是,假设你已经有了一个动物类/接口-所以真的什么if告诉你,是你需要创建一个接口。

所以,在现实世界中,什么样的if我们看到的就做,导致我们的多态性的解决方案?

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

这的确是刺激性看到整个代码。 怎么样,而是具有两个(或更多)记录器的实现?

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

这导致我们的策略模式

代替:

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

... 你可以有 ...

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

当然getCreditCheckStrategy()可能包含一个if -这很可能是合适的。 你推到它所属的一个整洁的地方。



Answer 16:

我认为,如果语句是罪恶的,但如果表情都没有。 我的意思是由什么,如果在这种情况下,表达可以是类似的C#三元操作符(条件trueExpression:falseExpression)。 这不是坏事,因为它是一个纯函数(在数学意义上)。 它的计算结果为一个新的价值,但对其他没有任何作用。 正因为如此,它工作在一个替代模式。

当务之急如果报表是邪恶的,因为他们迫使你创建的副作用,当你不需要。 对于if语句是有意义的,你必须出示取决于条件表达式不同的“效果”。 这些影响可能是一些类似IO,图形渲染或数据库事务,改变了计划以外的事情。 或者,它可能是变异现有变量的状态赋值语句。 它通常是更好的影响降至最小,他们从实际的逻辑中分离出来。 但是,因为如果语句,我们可以自由无处不在的代码添加这些“有条件地执行效果”。 我认为这是很糟糕。



Answer 17:

好,在Ruby中 ,我们有除非 ;)

但严重的可能,如果是下一个跳转 ,即使大多数人认为这是在某些情况下,邪恶简化/加快东西(在某些情况下,如低的水平高度优化的代码,这是一个必须)。



Answer 18:

如果不是邪恶! 考虑...

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

无聊的,是吗? 现在有增加,如果...

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

...您的代码创建的生产率(以LOC测量)加倍。

另外代码的可读性提高了很多,现在你可以在眼睛的结果就是当两个参数都为零的眨眼见。 你可以这样做,在上面的代码,是吗?

此外,您支持testteam因为他们现在可以把自己的代码覆盖测试工具使用起来更发挥到极致。

此外,该代码现在是未来的改进更充分的准备。 让我们来猜测,例如,总和应该是零,如果其中一个参数是零(不要笑,不要怪我,傻了客户的要求,你知道,顾客永远是对的)。 因为如果在第一时间只需要轻微的代码更改。

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

多少会被需要更多的代码更改,如果您还没有发明出的,如果从一开始。

感恩将是你在各方面。

结论:永远都不够,如果的。

你去那里。 至。



文章来源: Why is the 'if' statement considered evil?