9

如果我有一个带有大量条件的函数,那么组织它的最佳方式是什么?

我担心的是其他人进入代码并了解正在发生的事情。尽管示例很简单,但可以想象条件非常复杂。

例如:

public void function(string value, string value2)
{
    if (value == null)
        return;

    if (value2 == value)
        DoSomething();
}

或者

public void function(string value, string value2)
{
    if (value != null)
    {
        if (value2 == value)
            DoSomething();
    }
}

或者

public void function(string value, string value2)
{
    if (value != null && value2 == value)
        DoSomething();
}
4

11 回答 11

8

我更喜欢第一个选项 -快速失败更简洁、更清晰、更易于阅读和理解。

我知道这不是失败,但这个概念仍然适用。我真的一点也不喜欢嵌套if语句。

于 2009-06-23T14:58:03.647 回答
8

您可以查看防御性编程以确保可以实现方法功能的合同。

public void function(string value, string value2)
{
    if (string.IsNullOrEmpty(value1)) throw new ArgumentNullException("value1", "value 1 was not set");
    if (string.IsNullOrEmpty(value2)) throw new ArgumentNullException("value2", "value 2 was not set");

    DoSomething();
}
于 2009-06-23T15:00:37.010 回答
8

组织条件并将它们放入方法中。

例如替换这个:

 if( a& & n || c  && ( ! d || e ) && f > 1 && ! e < xyz ) { 
      // good! planets are aligned.
      buyLotteryTicket();
 } else if( ..... oh my ... ) { 
 }

进入这个:

if( arePlanetsAligned() ) { 
    buyLotteryTicket(); 
} else if( otherMethodHere() ) { 
   somethingElse();
}  

这样,您使用哪种样式( 1、2 或 3 )并不重要,因为 if 语句将清楚地描述正在测试的条件。不需要额外的构造。

关键是使代码更清晰和自我记录。如果您使用的是 OO 编程语言,则可以使用对象来存储状态(变量)并避免创建采用 5 到 10 个参数的方法。

这些是类似的问题:

摆脱嵌套 if 的最佳方法

是否有替代此超识别代码的方法

第二个链接显示了一种更完整和更复杂的方法,可以将可怕的每个维护者的噩梦转变为自我记录的代码。

它显示了如何转换它:

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            //now actually do stuff!
                            message = result_of_stuff_actually_done;
                        }
                    }
                }
            }
        }
    }
    return output;
}

进入这个:

if ( isValidInput() && 
    isRuleTwoReady() &&
    isRuleTreeDifferentOf( BAD_OBJECT ) &&
    isRuleFourDifferentOf( BAD_VALUE ) && 
    isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
            message = resultOfStuffActuallyDone();
}
于 2009-06-23T15:09:41.203 回答
2

将这些东西重构到它自己的函数中。阅读描述性函数名称比阅读一堆布尔表达式要好得多。

// leave complex conditional code out, so that we can focus on the larger problem in the function
public void function(string value, string value2)
{
    if (MyDescriptiveTestName)
    {
        DoSomething();
    }
}

// encapsulate complex conditional code so that we can focus solely on it.
private bool MyDescriptiveTestName(string value, string value2)
{
    if (value != null && value2 == value)
    {
        return true;
    }
    return false;
}
于 2009-06-23T15:07:45.983 回答
2

我可以推荐 Robert C. Martin 的书Clean Code,它为编写可读和可维护的代码提供了一套很好的启发式方法。

现在另一种选择是将条件提取到另一个私有函数中并命名它,以便它描述您的意图。它不适用于提供的代码,因为它是通用的,但它看起来像:

public void function(string value, string value2)
{
    if (valuesAreValidAndEqual(value, value2))
    {
        DoSomething();
    }
}

private void valuesAreValidAndEqual(string value, string value2)
{
    return value != null && value2 == value;
}

显然,如果变量名和函数名与您的域相关,这将更有用。

于 2009-06-23T15:10:56.617 回答
1

如果你有一个有很多条件的函数,我会使用一个switch语句——而不是 ifs。如果可能的话,我也可能将细节分解为几个函数(甚至类)。

相关堆栈溢出文章

于 2009-06-23T15:04:24.990 回答
0

我喜欢第三种选择,但这真的取决于语言。您假设在第三个中语​​句的第一部分将失败并且不执行第二部分。这取决于语言。我知道大多数基于 C 的语言都可以做到这一点,但是由于您没有指定一种潜在的问题。可能有一个我不知道的没有短路概念。

于 2009-06-23T15:01:51.343 回答
0

您提前考虑代码可读性的事实是成功的一半。

至于您的哪个示例最具可读性,这是非常主观的。

我对给出的例子的看法:

  • 就个人而言,我认为第一个例子最容易理解。
  • 最小化嵌套级别和/或条件的数量通常会提高可读性。
  • 有些人会反对方法的多个退出点(如示例 1),但我认为只有当方法变得非常长时才会成为问题。如果您只是检查输入并快速失败,这并不是什么大问题。
于 2009-06-23T15:05:54.097 回答
0

我的偏好是第二种选择。就个人而言,当我阅读这样的代码时,我会记住进入每个嵌套级别的条件。在第一个示例中,我可能会忘记第一个条件(值 == null 为假)继续成立。第三个也不错,但我更喜欢第二个。

于 2009-06-23T15:07:29.850 回答
0

一个函数中有这么多语句的事实表明该函数应该被分成更小的函数。

没有放置 if 语句的最佳方式,我认为答案是主观的。

于 2009-06-23T15:10:57.007 回答
0

清晰度通常是一个难以判断的品质。当你编写一段代码时,你清楚的东西可能对其他人来说是完全迟钝的 - 甚至在经过足够的时间之后对你自己来说也是如此。

以下是我在构建具有许多条件检查的函数时的个人经验法则:

  1. 尽可能将条件分组为逻辑单元
  2. 使用明确命名的中间值来简化可读性和调试。
  3. 避免多次重复相同的逻辑结构(见上文)。
  4. 如果可能,将复杂或昂贵的条件重构为单独的函数。
  5. 如果可能,请在方法顶部使用提前退出条件退出,但是...
  6. 避免在整个方法体中使用返回语句。
  7. 不要依赖运算符优先级,使用括号对语句进行分组。
  8. 避免依赖缩进,即使只有一个语句,也要使用块({...}),这有助于可维护性。
于 2009-06-23T17:35:37.357 回答