【问题标题】:Is it good to use lots of and condition in if statement?在 if 语句中使用大量和条件是否很好?
【发布时间】:2015-08-11 07:31:37
【问题描述】:

我有一个类似这样的代码,我只是想知道在单个 if 语句中使用大量“和”“或”是否是个好习惯

if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
            array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
            ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
                (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
        ) {

        }

或者有没有更好的方法?

【问题讨论】:

  • 在可读性或性能方面的“好习惯”还是...?
  • 你输入的条件越多,理解代码就越困难,但除非你做对了并且知道你在做什么,否则事情不会按照你的计划进行的风险。越复杂越难,但也更精确:)
  • @Fildor 可读性为主。
  • 好问题@user5064303。但是,我从可读性和性能的角度看到了响应。这将有助于更新您的问题以解释您是否要求这些领域中的任何一个或两个。

标签: php arrays if-statement


【解决方案1】:

建议避免使用复杂的条件表达式,因为它们难以阅读和理解。但是,如果表达式只使用一种类型的逻辑运算符来连接条件,那就很容易理解了。

例如:

if ($a && $b || $c && ! $d) {
    echo("Yes!");
}

您能一眼看出if 分支(echo())中的代码何时会执行吗?很可能没有。该表达式包含所有逻辑运算的混合,如果不绘制$a$b$c$d 的所有可能值的表,则很难评估其最终值。

另一方面,表达式:

if ($a && $b && $c && $d) {
    echo("Hooray!");
}

更容易掌握。
当所有$a$b$c$d 同时为TRUE 时,就会执行echo() 语句。

重构

在您的情况下,情况比上面介绍的第一个更复杂。它包含所有逻辑运算符和括号,子表达式很长。

为了保持可读性,我会将复杂条件提取到返回布尔值(谓词)的(​​私有或受保护)方法中。

private function isValidData(array $data)
{
    if (array_key_exists(self::SUB_FORM_CONTACT, $data) &&
        array_key_exists('company', $data[self::SUB_FORM_CONTACT]) &&
        ((array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion'])) ||
            (array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) && !empty($data[self::SUB_FORM_CONTACT]['company'][''])))
    ) {
        return TRUE;
    } else {
        return FALSE;
    }
}

并替换if 语句中的表达式:

if ($this->isValidData($data)) {
}

在接下来的步骤中,我会将复杂的条件表达式拆分为更小的条件,逐个测试它们,并尽可能早地使函数 isValidData() 返回 (FALSE)。

步骤 1

因为表达式很复杂,而且片段相对较长,所以很难按原样处理。这就是为什么在第一步中我会将条件提取到名称较短的局部变量中($a$b a.s.o. 从上面):

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $b = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
    $c = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $d = empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
    $e = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']);
    $f = empty($data[self::SUB_FORM_CONTACT]['company']['']);
    // this should probably read 'serviceRegion' -------^

    if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    } else {
        return FALSE;
    }
}

现在一切看起来都清楚了,我们甚至发现了一个错误。太好了!

第二步

让我们尝试解开(现在更容易阅读)条件:

if ($a && $b && (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
} else {
    return FALSE;
}

如果我们一次进行一项测试并在结果明显后立即返回(TRUEFALSE),可能会更容易理解。 当$a$b 以及包含表达式其余部分的括号同时为TRUE 时,该函数返回TRUE

if 语句可以这样重写:

if ($a) {
    if ($b) {
        if (($c && ! $d) || ($e && ! $f))) {
            return TRUE;
        }
    }
}

return FALSE;

通过反转$a$b 的条件,我们可以提前返回FALSE

if (! $a) {
    return FALSE;
}
if (! $b) {
    return FALSE;
}

if (($c && ! $d) || ($e && ! $f))) {
    return TRUE;
}
return FALSE;

现在的代码如下所示:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);

    $c = array_key_exists('salesRegion', $form['company']);
    $d = empty($form['company']['salesRegion']);

    $e = array_key_exists('serviceRegion', $form['company']);
    $f = empty($form['company']['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if (($c && ! $d) || ($e && ! $f))) {
        return TRUE;
    }
}

请注意,因为从第二个开始的所有赋值都包含公共子表达式$data[self::SUB_FORM_CONTACT],所以我们将其提取到一个局部变量中。最后4个赋值也是一样的(提取公共子表达式$form['company']

// ...
$b = array_key_exists('company', $form);
$company = $form['company'];

$c = array_key_exists('salesRegion', $company);
$d = empty($company['salesRegion']);

$e = array_key_exists('serviceRegion', $company);
$f = empty($company['serviceRegion']);

第三步

让我们注意在表达式$c && ! $d:

array_key_exists('salesRegion', $company) && ! empty($company['salesRegion'])

array_key_exists() 的调用并不是真正需要的。我猜它的目的是避免在访问$company['salesRegion'] 并且$company 不包含密钥'salesRegion' 时触发通知。 empty() PHP 结构会在数组中不存在键且不会触发通知时愉快地返回 TRUE

$e && ! $f 也是如此。这使我们可以完全删除$c$e。现在的情况是这样的:

if (! $d || ! $f) {
    return TRUE;
}

或者

if (! ($d && $f)) {
    return TRUE;
}

通过否定条件:

if ($d && $f) {
    return FALSE;
} else {
    return TRUE;
}

现在完整的函数如下所示:

private function isValidData(array $data)
{
    $a = array_key_exists(self::SUB_FORM_CONTACT, $data);
    $form = $data[self::SUB_FORM_CONTACT];

    $b = array_key_exists('company', $form);
    $company = $form['company'];

    $d = empty($company['salesRegion']);
    $f = empty($company['serviceRegion']);


    if (! $a) {
        return FALSE;
    }
    if (! $b) {
        return FALSE;
    }

    if ($d && $f) {
        return FALSE;
    } else {
        return TRUE;
    }
}

最后一步

我们现在可以消除局部变量$a$b$d$f,并在我们知道它们不是NULL 之后移动$form$company 的初始化(每个后对应的调用array_key_exists()):

函数的最终形式是这样的:

/**
 * Verify if the provided data is valid.
 *
 * @param array $data the data to validate
 * @return boolean TRUE if the data is valid, FALSE if some field is missing or empty
 */
private function isValidData(array $data)
{
    // $data must contain the contact form information
    if (! (array_key_exists(self::SUB_FORM_CONTACT, $data))) {
        return FALSE;
    }

    // The contact form information exists in $data
    $form = $data[self::SUB_FORM_CONTACT];
    // The form must contain company information
    if (! (array_key_exists('company', $form))) {
        return FALSE;
    }

    // The company information exists in the contact form
    $company = $form['company'];
    // The company must contains information about salesRegion or serviceRegion or both
    if (empty($company['salesRegion']) && empty($company['serviceRegion'])) {
        return FALSE;
    }

    // The data is valid
    return TRUE;
}

结论

代码比以前长了几行,但是:

  • 条件更易于阅读和理解,因为它们是一个接一个地计算的,而不是在一个大而复杂的表达式中;
  • 代码被封装在一个函数中; if ($this->isValidData($data)) 条件的读者现在一目了然发生了什么,而无需阅读和理解它是如何发生的;如果它有效,请不要担心;
  • 验证代码可以独立于数据有效与否时发生的情况进行测试;
  • 如果需要和适当,可以通过调用具有不同数据的方法(可能通过不同的表单提交)来重用代码。

【讨论】:

    【解决方案2】:

    PHP 使 if 语句短路。因此,如果您有许多 if 子句。

       if ($condition1 && $condition2 && $condition3 && $condition4)
    

    那么一旦第一个表达式为假,PHP 就会停止计算表达式。

    要优化代码,请对表达式进行排序,以便首先评估最有可能失败的表达式。

    参考: http://php.net/manual/en/language.operators.logical.php

    Does PHP have short-circuit evaluation?

    Is there a short-circuit OR in PHP that returns the left-most value?

    【讨论】:

      【解决方案3】:

      就可读性而言,您应该正确格式化代码并且一切都应该没问题(给定示例中的格式化是可以的)。在调试方面,在定义哪个语句失败时可能会出现一些问题,但仍然 - 使用正确的格式,您可以简单地注释掉几行并检查结果。在性能的情况下,我认为它甚至更好。我的意思是无论如何你必须以某种方式检查所有这些条件。如果您将它们全部放在单独的 if 中,它们将被一个接一个地检查(好的,您可以编写它以便不是所有的都被检查,但我认为这只是不必要地延长了您的代码)。如果您正确地撰写您的陈述,则不必检查所有陈述。例如有很多&& 如果第一个条件为假,则甚至不检查下一个条件。您应该在开始时放置不太可能、更轻量级(就性能而言)的条件,并在最后留下重的、可能的条件。如果你想保持你的主代码干净,你也可以将它包装在一个函数中。

      【讨论】:

        【解决方案4】:

        这是一个性能不佳的解决方案。但是,一般来说,在处理表单时,如果它是地球上最有效的东西,我就不会那么在意——它不是内核代码。我刚刚解决了一个类似的问题,向它扔了 192 gigs 的 ram……在此基础上,它可以为我节省成百上千的开发时间,这比 ram 的成本要高得多。

        $hasdata            = array_key_exists(self::SUB_FORM_CONTACT, $data);
        $hasCompany         = array_key_exists('company', $data[self::SUB_FORM_CONTACT]);
        $hasSalesRegion     = array_key_exists('salesRegion', $data[self::SUB_FORM_CONTACT]['company']) 
        $hasSalesRegion     = $hasSalesRegion && !empty($data[self::SUB_FORM_CONTACT]['company']['salesRegion']);
        $hasServiceRegion   = array_key_exists('serviceRegion', $data[self::SUB_FORM_CONTACT]['company']) 
        $hasServiceRegion   = $hasServiceRegion && !empty($data[self::SUB_FORM_CONTACT]['company']);
        
        $evalResult = $hasdata && $hasCompany && ($hasSalesRegion || $hasServiceRegion);
        
        if($evalResult)
        {
            // do your thing
        }
        

        我更愿意看到这样的东西,而不是获得捷径和难以阅读的双重否定的好处。

        【讨论】:

          猜你喜欢
          • 2020-01-28
          • 2015-06-24
          • 2015-07-08
          • 1970-01-01
          • 1970-01-01
          • 2018-04-19
          • 1970-01-01
          • 1970-01-01
          • 2018-09-26
          相关资源
          最近更新 更多