else 永远不是必需的,您可以简化代码以在没有 else 的情况下工作

Jul*_*oro 3 php laravel phpmd

我收到一条 PHPMD 消息告诉我:

else 永远不是必需的,您可以简化代码以在这部分代码上没有 else 的情况下工作:

if ($settings == null) {
        $settings = new self($arrSettings);
} else {
        $settings->fill($arrSettings);
}
$settings->save();

return $settings;
Run Code Online (Sandbox Code Playgroud)

我的问题是:我应该如何避免 else()。我看到的唯一方法是复制$setting->save()和返回。

任何的想法?

fla*_*ovs 6

TL,博士

\n\n
    \n
  • PHPMD 有一点道理
  • \n
  • 初衷是好的,但PHPMD else“永远没有必要”的建议是错误的
  • \n
  • 您始终可以删除else- 但很多时候这会导致更混乱的代码(这很讽刺,因为您将遵循“混乱检测器”的建议)。
  • \n
  • 所以你应该经常问自己:我应该避免这种情况吗else
  • \n
  • 连PHPMD本身都用else所以看起来有时候还是有必要的
  • \n
\n\n

长答案

\n\n

else是一种非常标准的语言结构,用于许多语言和软件包(包括 PHPMD 本身)。对我来说,说else永远没有必要就等于说do...while永远没有必要,因为你可以结合while (true)break,这是事实,但毫无意义。因此,我对此建议持保留态度,并在仅根据静态分析器建议更改任何软件之前进行反思。

\n\n

综上所述,我认为 PHPMD 开发人员的意思是,在许多情况下,您可以而且应该else从代码中删除以使其更具可读性。

\n\n

最简单的情况是:

\n\n
    if ($condition) {\n        $a = 1;\n    } else {\n        $a = 2;\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

可以简化为:

\n\n
    $a = ($condition ? 1 : 2);\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在看看这个表达式:

\n\n
    // Calculate using different formulas, depending on $condition.\n    if ($condition) {\n        // Calculate using secret formula.\n        $a = power($r * M_PI, 2) / sqrt(exp($k));\n    } else {\n        // Calculate using standard formula.\n        $a = power(($r / $k) * M_PI, 2) / sqrt(1 / $k);\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可以更改为:

\n\n
    $a = ($condition ? power($r * M_PI, 2) / sqrt(exp($k)) : power(($r / $k) * M_PI, 2) / sqrt(1 / $k));\n
Run Code Online (Sandbox Code Playgroud)\n\n

当然,第二种形式更简洁,或者我应该说,更小。但从代码清晰度和可维护性的角度来看,我想带有 an 的原始代码else要清晰得多,更不用说使用代码注释解释“改进”版本要困难得多,不是吗?

\n\n

恕我直言,是的。我总是else在这样的情况下使用。

\n\n

另一个简单的例子:

\n\n
    // Check animal first.\n    if ($animal === \'dog\') {\n        if ($breed === \'pinscher\') {\n            $weight = \'light\';\n        } else {\n            $weight = \'heavy\';\n        }\n    } else {\n        // We only deal with dogs.\n        $weight = "we cannot say anything about $animal";\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

没有其他版本:

\n\n
    $weight = ($animal === \'dog\' ? ($breed === \'pinscher\' ? \'light\' : \'heavy\') : "we cannot say anything about $animal");\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,在这种情况下,没有 else 的版本直接违反了 PSR-2,它禁止嵌套三元运算符。

\n\n

通常,人们会保留简单的结构,否则可以用三元运算符替换,只是因为人们想避免代码中出现长行,这不利于代码的可读性:

\n\n
    if (sqrt($weight) > 30 && $animal === \'elephant\' && $location == \'zoo\') {\n        $visitors_max = sqrt($guards) / ($ticker_price * M_2_SQRTPI)\n    } else {\n        $visitors_max = $visitors_max_last_year * exp($ticket_price) * 1.1;\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

这就变成:

\n\n
    $visitors_max = (sqrt($weight) > 30 && $animal === \'elephant\' && $location == \'zoo\') ? sqrt($guards) / ($ticker_price * M_2_SQRTPI) : $visitors_max_last_year * exp($ticket_price) * 1.1);\n
Run Code Online (Sandbox Code Playgroud)\n\n

接下来,我想 PHPMD 希望您解决另一个众所周知的模式:

\n\n
\n    function myfunc($arg)\n    {\n        if ($arg === \'foo\') {\n            $res = \'foo found\';\n        } else {\n            $len = strlen($arg);\n        if ($len > 10) {\n            $res = \'arg is too big\';\n        } else {\n            $bar = dosomething($res);\n            $res = "$arg results in $bar";\n        }\n        return $res;\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

该函数使用了曾经在编程课程中教授的一个建议,即函数应该有一个出口点,因为这(可以说)使得更容易理解程序流程和发现错误。

\n\n

恕我直言(和 PHPMD),我们可以删除else并提高代码的清晰度/可维护性,这样的代码不会丢失任何东西:

\n\n
\n    function myfunc($arg)\n    {\n        if ($arg === \'foo\') {\n            return \'foo found\';\n        }\n\n        $len = strlen($arg);\n        if ($len > 10) {\n        return \'arg is too big\';\n        }\n\n        $bar = dosomething($res);\n        return "$arg results in $bar";\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

但这可能并不总是理想的:

\n\n
\n    function mycalc($n)\n    {\n        if ($n === 0) {\n           $multiplier = 0.5;\n        } elseif ($n === 1) {\n           $multiplier = M_LN2;\n        } else {\n           $multiplier = power(sqrt($n * M_PI), 2);\n        }\n\n        return $multiplier * (M_2_PI * power($n * M_PI, 2));\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

“改进”版本应该是这样的:

\n\n
\n    function mycalc($n)\n    {\n        if ($n === 0) {\n           return 0.5 * (M_2_PI * power($n * M_PI, 2));\n        }\n\n        if ($n === 1) {\n           return M_LN2 * (M_2_PI * power($n * M_PI, 2));\n        }\n\n        return power(sqrt($n * M_PI), 2) * (M_2_PI * power($n * M_PI, 2));\n    }\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不确定你的想法,但是第一个版本更紧密地遵循我的计算思路,因此比第二个版本更容易理解和维护,尽管它使用了“禁止” else

\n\n

(有人可能会争辩说,我们可以使用第二种形式,加上一个辅助变量来保存公共计算。这很公平,但人们总是可以反驳说,添加不必要的变量会使代码不太清晰,维护成本也更高。)

\n\n

那么,为了回答你的问题,我应该如何避免其他情况?,我会问另一个:你为什么要这么做?

\n\n

@tere\xc5\xa1ko 的回答有一定道理,并且确实使代码更加简洁。然而,我个人认为你的第一个版本非常好,其意图更明确,因此从可理解性和可维护性的角度来看要好得多:

\n\n
\n    if (I do not have $object)\n        create a new $object with my settings\n    else\n        call the "fill" method of $object with my settings\n    endif\n    do stuff with $object\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

相对:

\n\n
\n    if (I do not have $object)\n        create a new $object\n    endif\n\n    call the "fill" method of $object with my settings\n    do stuff with $object\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

另请注意,没有上述内容的版本中的编程逻辑有一个微妙的变化else:您(和所有未来的开发人员)必须假设使用我的设置调用 $object 的“fill”方法,并始终使用我的设置创建一个新的 $object最终得到一个具有相同内部状态的对象。原始版本中不需要这个假设。

\n\n

换句话说,只要fill()方法和对象的构造函数对对象的内部状态执行相同的操作,重构的代码就会起作用,这可能是真的,也可能不是真的——现在或永远。

\n\n

为了说明这一点,假设该对象定义如下:

\n\n
\n    if (I do not have $object)\n        create a new $object with my settings\n    else\n        call the "fill" method of $object with my settings\n    endif\n    do stuff with $object\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,您的原始版本和没有的版本else最终会得到具有不同内部状态的对象,并且发现错误会更加困难,因为它将隐藏在假设和隐式构造后面。

\n\n

现在,让我们看一下 PHPMD 自己的else

\n\n
\n    if (I do not have $object)\n        create a new $object\n    endif\n\n    call the "fill" method of $object with my settings\n    do stuff with $object\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题是:我们应该避免这种情况else吗?

\n


ter*_*ško 5

可能是因为它可以重写为

if ($settings === null) {
    $settings = new self; // or new self([]);
}
$settings->fill($arrSettings);
$settings->save();

return $settings;
Run Code Online (Sandbox Code Playgroud)

但是,TBH,整个事情看起来像是对SRP 的一次重大违反,因为类实例不应该能够创建自己的新实例。那只是没有任何意义..但话又说回来,我不是“工匠”。