关于编码风格:如果函数中的语句有什么最好的方法

If statements in a function what is the best way

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

我担心的是有人进入代码并理解正在发生的事情。尽管这个例子很简单,但是假设条件非常复杂。

举个例子:

1
2
3
4
5
6
7
8
public void function(string value, string value2)
{
    if (value == null)
        return;

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

1
2
3
4
5
6
7
8
public void function(string value, string value2)
{
    if (value != null)
    {
        if (value2 == value)
            DoSomething();
    }
}

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

把这些条件组织起来,形成一种方法。

例如,替换此:

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

进入这个:

1
2
3
4
5
if( arePlanetsAligned() ) {
    buyLotteryTicket();
} else if( otherMethodHere() ) {
   somethingElse();
}

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

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

这些问题类似:

消除嵌套IFS的最佳方法

有没有别的办法来代替这个超识别码

第二个链接1显示了一种更完整和复杂的方法,将恐怖的"每个人维护者"的噩梦转换成一个自我记录的代码。

它显示了如何转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
}

进入这个:

1
2
3
4
5
6
7
if ( isValidInput() &&
    isRuleTwoReady() &&
    isRuleTreeDifferentOf( BAD_OBJECT ) &&
    isRuleFourDifferentOf( BAD_VALUE ) &&
    isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) {
            message = resultOfStuffActuallyDone();
}


您可以查看防御编程,以确保方法功能的契约能够得到满足。

1
2
3
4
5
6
7
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();
}


我更喜欢第一种选择——快速失败更干净、更清晰、更容易阅读和理解。

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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
public void function(string value, string value2)
{
    if (valuesAreValidAndEqual(value, value2))
    {
        DoSomething();
    }
}

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

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


把这些东西重构成它自己的函数。读一个描述性的函数名比读一堆布尔表达式要好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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;
}


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

相关堆栈溢出项目:

  • "else if"是否比"switch()case"更快?
  • 何时使用if else if else切换开关柜,反之亦然


清晰往往是难以判断的品质。当你写一段代码时,你所清楚的事情可能会完全被其他人所蒙蔽,甚至在足够长的时间过后你自己也会如此。

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

  • 尽可能将条件分组为逻辑单元
  • 使用明确命名的中间值简化可读性和调试。
  • 避免多次重复相同的逻辑结构(见上文)。
  • 如果可能,将复杂或昂贵的条件重构为单独的函数。
  • 如果可能,使用方法顶部的早期退出条件退出,但…
  • 避免在整个方法体中重复返回语句。
  • 不要依赖于运算符优先级,请使用括号对语句进行分组。
  • 避免依赖缩进,即使只使用一条语句,也要使用块(…),这有助于维护性。

  • 在一个函数中有这么多语句,这是一个迹象,表明该函数应该划分为较小的语句。

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


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


    事实上,您提前考虑代码的可读性是其中的一半。

    至于你的例子中哪一个可读性最好,这是非常主观的。

    我对所给例子的看法:

    • 就个人而言,我认为第一个例子最容易跟随。
    • 最小化嵌套级别和/或条件数通常增强可读性。
    • 有些人会反对多元化方法的退出点(如例1),但我认为当方法变得很长。它不是真的如果你只是检查输入并快速失败。

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