在条件语句中执行函数被认为是不好的形式吗?

时间:2010-03-24 01:45:32

标签: language-agnostic if-statement

考虑一种情况,你需要调用连续的例程,并在返回一个可以被评估为正值的值时停止(true,object,1,str(1))。

这样做非常诱人:

if (fruit = getOrange())
elseif (fruit = getApple())
elseif (fruit = getMango())
else fruit = new Banana();

return fruit;

我喜欢它,但这不是一个非常经常的风格,可以被认为是专业的生产代码。人们可能更愿意看到更精细的代码,如:

fruit = getOrange();
if(!fruit){
    fruit = getApple();
    if(!fruit){
        fruit = getMango();
        if(!fruit){
            fruit = new Banana();
        }
    }
}

return fruit;

根据基本结构的教条,以前的形式是否可以接受?你会推荐吗?

修改

我向那些认为这些职能是工厂或建筑师的人道歉。他们不是,他们只是占位符。问题更多的是语法而不是“工厂化”。这些函数也可以是lambda。

7 个答案:

答案 0 :(得分:9)

如果你想要一个简洁的语法,几种语言允许使用“逻辑或”为此目的(C#显式提供了一个合并运算符,因为空值不是假的)。

的Python:

fruit = ( getOrange() or 
          getApple()  or 
          getMango()  or 
          Banana() )

C#:

fruit = getOrange() ?? 
        getApple()  ?? 
        getMango()  ?? 
        new Banana();

答案 1 :(得分:4)

我可以想到两种选择。

第一种只允许使用像你这样的语言(PHP?),其中single = in条件就可以了。

    if ( (fruit = getOrange()) != null)
    elseif ( (fruit = getApple()) != null)
    elseif ( (fruit = getMango()) != null)
    else fruit = new Banana();

明确表示您正在进行比较,并且单个=不是错误。

    fruit = getOrange();
    if(!fruit) fruit = getApple();
    if(!fruit) fruit = getMango();
    if(!fruit) fruit = new Banana();

就像你的第二个例子,但摆脱了丑陋的额外嵌套。

答案 2 :(得分:3)

在强类型语言中,0 / null不等于false,非0 /非null等于true,我会说它可能是安全的,但在一般情况下可读性略低,其中你的方法名称和参数数量可能更大。我个人会避免它,除了某些标准习惯用法,在0 / null等于false和非0 /非null为真的情况下,仅仅因为在阅读代码时可能会混淆赋值与等式检查。弱类型语言中的一些习语,如C语言,如此普遍,以至于避免它们是没有意义的,.e.g,

 while ((line = getline()) != null) {
   ...
 }

答案 3 :(得分:1)

正如我所看到的,这个问题不是结构,而是驱动规则。为什么getOrange会出现在getApple等之前?

您可能更有可能看到更多数据驱动的内容:

enum FruitEnum
{
  Orange, Apple, Mango, Banana
}

并单独列出

List<FruitEnum> orderedFruit = getOrderedFruit();
int i = 0;
FruitObj selectedFruit;
while(selectedFruit == null && i <= orderedFruit.Count)
{
    fruit = FruitFactory.Get(orderedFruit[i++]);
}
if(fruit == null)
{
    throw new FruitNotFoundException();
}

也就是说,为了简化代码,您可以使用合并运算符:

fruit = getOrange() ?? getApple() ?? getMango() ?? new Banana();

答案 4 :(得分:1)

在C或C ++中,你可以写:

return (fruit = getOrange()) ? fruit :
       (fruit = getApple())  ? fruit :
       (fruit = getMango())  ? fruit :
        new Banana();

避免这个和你的第一个版本的原因不是“基本结构上的教条”,而是在条件中自行分配令人困惑。一方面,并​​非所有语言都支持它。对于另一个,它很容易被误读为==,或者读者可能不确定你是否真正意义,或者可能是==。为每个条件添加!= 0变得非常密集和冗长。

GCC有一个允许的扩展名:

return getOrange() ? : getApple() ? : getMango() ? : new Banana();

||or通常可以实现同样的目的(但不能用C或C ++)。

另一种可能性是:

do {
    fruit = getOrange();
    if (fruit) break;
    fruit = getApple();
    if (fruit) break;
    fruit = getMango();
    if (fruit) break;
    fruit = new Banana();
} while (false);

使用Perl中的break可以last出一个基本块的语言会更好,因为您可以省略do / while(false)。但可能只有汇编程序员才会喜欢它。

答案 5 :(得分:0)

直接回答你的问题:在条件陈述中出现副作用通常是 坏形式。

作为一种解决方法,您可以将水果构造函数存储在数组中,并找到第一个返回非null(伪代码)的构造函数:

let constructors = [getOrange; getApple; getMango; fun () -> new Banana()]
foreach constructor in constructors
    let fruit = constructor()
    if fruit != null
        return fruit

它像null-coalesce运算符,但更通用。在C#中,您可能会使用linq,如下所示:

 var fruit = constructors
     .Select(constructor => constructor())
     .Filter(x => x != null)
     .First();

至少通过这种方式,您可以像工厂类一样传递构造函数,而不是使用null-coalesce运算符对它们进行硬编码。

答案 6 :(得分:0)

我认为没有人提到第一个版本在调试器中可能会有点痛苦。可读性的差异是有争议的,但总的来说,我在条件中避免赋值和函数调用,以便在必要时更容易跟踪执行(即使我从不需要调试它,其他人可能需要稍后修改代码)。