替换嵌套的if语句

38
这与美丽的代码一章有关。在那一章中,我读到了关于嵌套if语句的内容。
作者谈到深度嵌套的if语句是错误和难以阅读的根源。他提到用case语句和决策表替换嵌套的if语句。
有人能说明如何使用caseselect case)和决策表来消除嵌套的if语句吗?
11个回答

30

好的,虽然你具体问的是关于switch/case语句的问题,但这里有一个类似的问题。

反转“if”语句以减少嵌套

这里介绍了使用守护语句替换嵌套的if语句,它们会提前返回,而不是逐步检查更多内容,最后才选择返回值。


14

我总是尝试做一个例子,就是像这样替换掉嵌套的if语句(实际上这个例子并不糟糕,但是我在真实场景中见过8或9层的嵌套):

if (i == 1) {
    // action 1
} else {
    if (i == 2) {
        // action 2
    } else {
        if (i == 3) {
            // action 3
        } else {
            // action 4
        }
    }
}

使用这个:

switch (i) {
    case 1:
        // action 1
        break;
    case 2:
        // action 2
        break;
    case 3:
        // action 3
        break;
    default:
        // action 4
        break;
}

我也尽量将操作保持尽可能小(函数调用最好),以便压缩 switch 语句(这样您就不必翻四页才能看到它的结尾)。

我认为,决策表只是设置指示后续需要执行哪些操作的标志。"后续" 部分是基于这些标志的简单顺序操作。我可能错了(这不是第一次或最后一次:-)。

例如(标志设置阶段可能会很复杂,因为其操作非常简单):

switch (i) {
    case 1:
        outmsg = "no paper";
        genmsg = true;
        mailmsg = true;
        phonemsg = false;
        break;
    case 2:
        outmsg = "no ink";
        genmsg = true;
        mailmsg = true;
        phonemsg = false;
        break;
    default:
        outmsg = "unknown problem";
        genmsg = true;
        mailmsg = true;
        phonemsg = true;
        break;
}

if (genmsg)
    // Send message to screen.
if (mailmsg)
    // Send message to operators email address.
if (phonemsg)
    // Hassle operators mobile phone.

3
因为“else if”很麻烦,而且必须写成“else{if...}”?但是像这样简单的if语句最好使用switch语句。 - JeeBee

9
将条件转换为布尔值,然后为每种情况编写布尔表达式。
如果代码是:
if (condition1)
{
    do1
}   
else
{
    if (condition2)
    {
        do2
    }
    else (condition3)
    {
        do3;

    }
}

可以这样写:
bool cond1=condition1;
bool cond2=condition2;
bool cond3=condition3;

if (cond1) {do1;}
if (!cond1 and cond2) {do2;}
if (!cond1 and cond3) {do2;}

3
当存在很多条件时,这会变得非常丑陋。此外,可读性也会因为实际条件被隐藏在布尔值后面而受到影响。 - Daniel Becroft
1
@DanielBecroft,我实际上有相反的看法。我同意Jeff Atwood避免注释的方法,为了做到这一点,你给那个布尔变量起一个有意义的名字,那会增强可读性...话虽如此,这里提出的方法并不是万能的。如果你不想无谓地计算条件怎么办? - Mazyod
同意 @Mazyod 的观点,上述方法的缺点是条件会被不必要地计算。在Scala中,可以使用lazy val来解决这个问题。 - khivi

9

如何处理连锁 if 语句?

替换为

if (condition1)
{
    do1
}   
else
{
    if (condition2)
    {
        do2
    }
    else (condition3)
    {
        do3;

    }
}

使用

if (condition1) {
   do1;
} else if (condition2) {
   do2;
} else if (condition3) {
   do3;
}

这很像用于复杂条件的switch语句。

3

2
您可以只在验证失败的一部分中断,例如。
function validate(){
  if(b=="" || b==null){
      alert("Please enter your city");
      return false;
  }

  if(a=="" || a==null){
      alert("Please enter your address");
      return false;
  }
  return true;
}

我不确定我喜欢这里有多个返回。 转而使用goto错误退出? - Larry_C

1

决策表是将条件逻辑存储在数据结构中,而不是在代码本身中。

因此,不使用 @Pax 的示例:

if (i == 1) {
    // action 1
} else {
    if (i == 2) {
        // action 2
    } else {
        if (i == 3) {
            // action 3
        } else {
            // action 4
        }
    }
}

你可以像这样做:

void action1()
{
    // action 1
}

void action2()
{
    // action 2
}

void action3()
{
    // action 3
}

void action4()
{
    // action 4
}

#define NUM_ACTIONS 4

// Create array of function pointers for each allowed value of i
void (*actions[NUM_ACTIONS])() = { NULL, action1, action2, action3 }

// And now in the body of a function somewhere...
if ((i < NUM_ACTIONS) && actions[i])
    actions[i]();
else
    action4();

如果可能的i不是低位整数,那么您可以创建一个查找表,而不是直接访问actions数组的第i个元素。
当您需要决定几十个可能值时,这种技术比嵌套的ifswitch语句更有用。

0

if和switch语句并不是纯粹的面向对象编程。它们是条件过程逻辑,但是它们做得非常好!如果你想要采用更加面向对象的方法来移除这些语句,可以结合“状态”和“描述符”模式


1
你把“不纯粹的面向对象”说得好像是一件坏事! - Javier
1
那就像是非常非常1986年的样子。你应该告诉人们,他们的代码现在不够实用。 - Daniel Earwicker
哈哈,我最近刚好读了这篇文章,觉得应该给出一个有针对性的回应。 - Anthony Mastrean

0

0

嵌套的if语句相当于逻辑运算符AND

if (condition1)
{
    if (function(2))
    {
        if (condition3)
        {
            // do something
        }
    }
}

等效的代码:

if (condition1 && function(2) && condition3)
{
    // do something
}

在这两种情况下,当一个表达式的值为false时,后续的表达式将不会被计算。例如,如果condition1是false,那么function()将不会被调用,condition3也不会被计算。

3
这句话的意思是:如果condition1为false,那么function(2)不会运行。但是在第二个例子中,所有三个函数都会运行,这可能会影响性能。 - rossanmol
不是的,@PHPLover - PHP使用惰性求值(有时称为短路求值),因此如果逻辑AND中的第一个条件为false,则不会尝试评估任何其他条件。同样,如果您正在执行OR操作并且第一个条件为true,则不会评估第二个条件。 - Demis Palma ツ
它们仍然不相等,我刚刚测试过了。如果第二个参数有错误,将显示一个错误,这意味着if语句仍然运行所有参数。 - rossanmol
@PHPLover 我无法想象你测试了什么样的代码以及你如何进行测试。惰性求值是一种古老而广为人知的特性,所有主要编程语言都具备这个特性,程序员在编写代码时必须考虑到它。您可以在http://sandbox.onlinephpfunctions.com/code/19e6e86d7ccb562325c8b8f4eea1e9539a5006fd上在线尝试PHP代码。嵌套IF和逻辑AND操作的行为相同。您能否提供您的代码片段来证明相反的情况? - Demis Palma ツ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接