else 永远不是必需的,您可以简化代码以在没有 else 的情况下工作
else is never necessary and you can simplify the code to work without else
我收到 PHPMD 的消息告诉我:
else is never necessary and you can simplify the code to work without else
on this portion of code:
if ($settings == null) {
$settings = new self($arrSettings);
} else {
$settings->fill($arrSettings);
}
$settings->save();
return $settings;
我的问题是:我应该如何避免 else()。我看到的唯一方法是复制 $setting->save()
和 return.
有什么想法吗?
可能是因为可以改写为
if ($settings === null) {
$settings = new self; // or new self([]);
}
$settings->fill($arrSettings);
$settings->save();
return $settings;
但是,老实说,整个事情看起来像是对 SRP 的一次重大违反,因为 class 实例不应该创建自己的新实例。那只是没有任何意义..但是话又说回来,我不是 "artisan".
TL,DR
- PHPMD 有一点
- 本意是好的,但是 PHPMD
else
"is never necessary" 的建议是错误的
- 您始终可以删除
else
——但很多时候这会导致代码更加混乱(这很讽刺,因为您将遵循 "mess detector" 的建议)。
- 所以你应该经常问自己:我应该避免这种情况吗
else
?
- 甚至 PHPMD 本身也使用
else
所以看起来 有时 这是必要的
长答案
else
是一种非常标准的语言结构,用于许多语言和软件包(包括 PHPMD 本身)。对我来说,说永远不需要 else
就等于说永远不需要 do...while
因为你可以组合 while (true)
和 break
,这是真的,但毫无意义.因此,我会半信半疑地接受这个建议,并在完全根据静态分析器的建议更改任何软件之前进行一些反思。
综上所述,我认为 PHPMD 开发人员的意思是,在许多情况下,您可以而且应该从代码中删除 else
以使其更具可读性.
最简单的情况是:
if ($condition) {
$a = 1;
} else {
$a = 2;
}
可以简化为:
$a = ($condition ? 1 : 2);
现在看看这个表达式:
// Calculate using different formulas, depending on $condition.
if ($condition) {
// Calculate using secret formula.
$a = power($r * M_PI, 2) / sqrt(exp($k));
} else {
// Calculate using standard formula.
$a = power(($r / $k) * M_PI, 2) / sqrt(1 / $k);
}
这可以更改为:
$a = ($condition ? power($r * M_PI, 2) / sqrt(exp($k)) : power(($r / $k) * M_PI, 2) / sqrt(1 / $k));
当然,第二种形式更简洁,或者我应该说,更小。但是从代码清晰度和可维护性的角度来看,我想带有 else
的原始代码要清晰得多,更不用说使用代码注释解释 "improved" 版本要困难得多,不是吗?
恕我直言,是的。 我总是在这种情况下使用else
。
另一个简单的例子:
// Check animal first.
if ($animal === 'dog') {
if ($breed === 'pinscher') {
$weight = 'light';
} else {
$weight = 'heavy';
}
} else {
// We only deal with dogs.
$weight = "we cannot say anything about $animal";
}
没有其他版本:
$weight = ($animal === 'dog' ? ($breed === 'pinscher' ? 'light' : 'heavy') : "we cannot say anything about $animal");
请注意,在这种情况下,没有 else 的版本直接违反了 PSR-2,它禁止嵌套的三元运算符。
人们经常保留简单的结构,否则可以用三元运算符替换,只是因为人们想避免代码中的长行,这不利于代码的可读性:
if (sqrt($weight) > 30 && $animal === 'elephant' && $location == 'zoo') {
$visitors_max = sqrt($guards) / ($ticker_price * M_2_SQRTPI)
} else {
$visitors_max = $visitors_max_last_year * exp($ticket_price) * 1.1;
}
变成:
$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);
继续,这是另一个众所周知的模式,我想 PHPMD 希望您解决:
function myfunc($arg)
{
if ($arg === 'foo') {
$res = 'foo found';
} else {
$len = strlen($arg);
if ($len > 10) {
$res = 'arg is too big';
} else {
$bar = dosomething($res);
$res = "$arg results in $bar";
}
return $res;
}
这个函数使用了曾经教过的关于编程的建议类,即函数应该有一个单一的退出点,因为这(可以说)更容易理解程序流程并找到错误。
恕我直言(和 PHPMD's),我们可以删除 else
并改进代码 clarity/maintainability,而不会丢失任何东西:
function myfunc($arg)
{
if ($arg === 'foo') {
return 'foo found';
}
$len = strlen($arg);
if ($len > 10) {
return 'arg is too big';
}
$bar = dosomething($res);
return "$arg results in $bar";
}
但这可能并不总是令人满意的:
function mycalc($n)
{
if ($n === 0) {
$multiplier = 0.5;
} elseif ($n === 1) {
$multiplier = M_LN2;
} else {
$multiplier = power(sqrt($n * M_PI), 2);
}
return $multiplier * (M_2_PI * power($n * M_PI, 2));
}
"improved" 版本应该是这样的:
function mycalc($n)
{
if ($n === 0) {
return 0.5 * (M_2_PI * power($n * M_PI, 2));
}
if ($n === 1) {
return M_LN2 * (M_2_PI * power($n * M_PI, 2));
}
return power(sqrt($n * M_PI), 2) * (M_2_PI * power($n * M_PI, 2));
}
我不确定你,但是第一个版本更接近我的计算思路,因此比第二个更容易理解和维护,即使它使用 "forbidden" else
.
(有人可能会争辩说我们可以使用第二种形式,加上一个辅助变量来保存通用计算。这很公平,但人们总是会反驳说添加一个不必要的变量会使代码更少 清晰且维护成本高。)
所以,为了回答你的问题 我应该如何避免 else?,我会问另一个问题:你为什么要避免?
@tereško 的回答有些道理,确实让代码更简洁了。但是,我个人认为您的第一个版本非常好,意图更明确,因此从可理解性和可维护性 POV 来看更好:
if (I do not have $object)
create a new $object with my settings
else
call the "fill" method of $object with my settings
endif
do stuff with $object
对比:
if (I do not have $object)
create a new $object
endif
call the "fill" method of $object with my settings
do stuff with $object
另请注意,在没有上述 else
的版本中,编程逻辑有细微的变化:您(和所有未来的开发人员)必须假设 调用 "fill" $object 的方法与我的设置 和 使用我的设置创建一个新的 $object 总是以具有相同内部状态的对象结束。原版不需要这个假设。
换句话说,只要 fill()
方法和对象的构造函数对对象的内部状态做同样的事情,重构的代码就可以工作,这可能是也可能不是——现在或永远.
为了说明这一点,假设对象定义如下:
class MyClass
{
protected $fillCount = 0;
protected $settings;
public function __construct(array $settings)
{
$this->settings = $settings;
}
public function fill(array $settings)
{
$this->fillCount++;
$this->settings = $settings;
}
}
在这种情况下,您的原始版本和没有 else
的版本最终将具有不同内部状态的对象,并且发现错误会更加困难,因为它会被隐藏假设和隐式结构背后。
现在,让我们来看看一个 PHPMD 自己的 else
:
// File: src/bin/phpmd
if (file_exists(__DIR__ . '/../../../../autoload.php')) {
// phpmd is part of a composer installation
require_once __DIR__ . '/../../../../autoload.php';
} else {
require_once __DIR__ . '/../../vendor/autoload.php';
// PEAR installation workaround
if (strpos('@package_version@', '@package_version') === 0) {
set_include_path(
dirname(__FILE__) . '/../main/php' .
PATH_SEPARATOR .
dirname(__FILE__) . '/../../vendor/pdepend/pdepend/src/main/php' .
PATH_SEPARATOR .
'.'
);
}
}
// (...100+ lines of code follows...)
问题是:我们应该避免这种情况吗else
?
在撰写本文时,为了让 phpmd 满意,只需使用 elseif
而不是 else
(当第一个 if
的正文很长时,在可读性方面有意义)
$condition = ... ;
if ($condition) {
...
} elseif (!$condition) {
...
}
我收到 PHPMD 的消息告诉我:
else is never necessary and you can simplify the code to work without else on this portion of code:
if ($settings == null) {
$settings = new self($arrSettings);
} else {
$settings->fill($arrSettings);
}
$settings->save();
return $settings;
我的问题是:我应该如何避免 else()。我看到的唯一方法是复制 $setting->save()
和 return.
有什么想法吗?
可能是因为可以改写为
if ($settings === null) {
$settings = new self; // or new self([]);
}
$settings->fill($arrSettings);
$settings->save();
return $settings;
但是,老实说,整个事情看起来像是对 SRP 的一次重大违反,因为 class 实例不应该创建自己的新实例。那只是没有任何意义..但是话又说回来,我不是 "artisan".
TL,DR
- PHPMD 有一点
- 本意是好的,但是 PHPMD
else
"is never necessary" 的建议是错误的 - 您始终可以删除
else
——但很多时候这会导致代码更加混乱(这很讽刺,因为您将遵循 "mess detector" 的建议)。 - 所以你应该经常问自己:我应该避免这种情况吗
else
? - 甚至 PHPMD 本身也使用
else
所以看起来 有时 这是必要的
长答案
else
是一种非常标准的语言结构,用于许多语言和软件包(包括 PHPMD 本身)。对我来说,说永远不需要 else
就等于说永远不需要 do...while
因为你可以组合 while (true)
和 break
,这是真的,但毫无意义.因此,我会半信半疑地接受这个建议,并在完全根据静态分析器的建议更改任何软件之前进行一些反思。
综上所述,我认为 PHPMD 开发人员的意思是,在许多情况下,您可以而且应该从代码中删除 else
以使其更具可读性.
最简单的情况是:
if ($condition) {
$a = 1;
} else {
$a = 2;
}
可以简化为:
$a = ($condition ? 1 : 2);
现在看看这个表达式:
// Calculate using different formulas, depending on $condition.
if ($condition) {
// Calculate using secret formula.
$a = power($r * M_PI, 2) / sqrt(exp($k));
} else {
// Calculate using standard formula.
$a = power(($r / $k) * M_PI, 2) / sqrt(1 / $k);
}
这可以更改为:
$a = ($condition ? power($r * M_PI, 2) / sqrt(exp($k)) : power(($r / $k) * M_PI, 2) / sqrt(1 / $k));
当然,第二种形式更简洁,或者我应该说,更小。但是从代码清晰度和可维护性的角度来看,我想带有 else
的原始代码要清晰得多,更不用说使用代码注释解释 "improved" 版本要困难得多,不是吗?
恕我直言,是的。 我总是在这种情况下使用else
。
另一个简单的例子:
// Check animal first.
if ($animal === 'dog') {
if ($breed === 'pinscher') {
$weight = 'light';
} else {
$weight = 'heavy';
}
} else {
// We only deal with dogs.
$weight = "we cannot say anything about $animal";
}
没有其他版本:
$weight = ($animal === 'dog' ? ($breed === 'pinscher' ? 'light' : 'heavy') : "we cannot say anything about $animal");
请注意,在这种情况下,没有 else 的版本直接违反了 PSR-2,它禁止嵌套的三元运算符。
人们经常保留简单的结构,否则可以用三元运算符替换,只是因为人们想避免代码中的长行,这不利于代码的可读性:
if (sqrt($weight) > 30 && $animal === 'elephant' && $location == 'zoo') {
$visitors_max = sqrt($guards) / ($ticker_price * M_2_SQRTPI)
} else {
$visitors_max = $visitors_max_last_year * exp($ticket_price) * 1.1;
}
变成:
$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);
继续,这是另一个众所周知的模式,我想 PHPMD 希望您解决:
function myfunc($arg)
{
if ($arg === 'foo') {
$res = 'foo found';
} else {
$len = strlen($arg);
if ($len > 10) {
$res = 'arg is too big';
} else {
$bar = dosomething($res);
$res = "$arg results in $bar";
}
return $res;
}
这个函数使用了曾经教过的关于编程的建议类,即函数应该有一个单一的退出点,因为这(可以说)更容易理解程序流程并找到错误。
恕我直言(和 PHPMD's),我们可以删除 else
并改进代码 clarity/maintainability,而不会丢失任何东西:
function myfunc($arg)
{
if ($arg === 'foo') {
return 'foo found';
}
$len = strlen($arg);
if ($len > 10) {
return 'arg is too big';
}
$bar = dosomething($res);
return "$arg results in $bar";
}
但这可能并不总是令人满意的:
function mycalc($n)
{
if ($n === 0) {
$multiplier = 0.5;
} elseif ($n === 1) {
$multiplier = M_LN2;
} else {
$multiplier = power(sqrt($n * M_PI), 2);
}
return $multiplier * (M_2_PI * power($n * M_PI, 2));
}
"improved" 版本应该是这样的:
function mycalc($n)
{
if ($n === 0) {
return 0.5 * (M_2_PI * power($n * M_PI, 2));
}
if ($n === 1) {
return M_LN2 * (M_2_PI * power($n * M_PI, 2));
}
return power(sqrt($n * M_PI), 2) * (M_2_PI * power($n * M_PI, 2));
}
我不确定你,但是第一个版本更接近我的计算思路,因此比第二个更容易理解和维护,即使它使用 "forbidden" else
.
(有人可能会争辩说我们可以使用第二种形式,加上一个辅助变量来保存通用计算。这很公平,但人们总是会反驳说添加一个不必要的变量会使代码更少 清晰且维护成本高。)
所以,为了回答你的问题 我应该如何避免 else?,我会问另一个问题:你为什么要避免?
@tereško 的回答有些道理,确实让代码更简洁了。但是,我个人认为您的第一个版本非常好,意图更明确,因此从可理解性和可维护性 POV 来看更好:
if (I do not have $object)
create a new $object with my settings
else
call the "fill" method of $object with my settings
endif
do stuff with $object
对比:
if (I do not have $object)
create a new $object
endif
call the "fill" method of $object with my settings
do stuff with $object
另请注意,在没有上述 else
的版本中,编程逻辑有细微的变化:您(和所有未来的开发人员)必须假设 调用 "fill" $object 的方法与我的设置 和 使用我的设置创建一个新的 $object 总是以具有相同内部状态的对象结束。原版不需要这个假设。
换句话说,只要 fill()
方法和对象的构造函数对对象的内部状态做同样的事情,重构的代码就可以工作,这可能是也可能不是——现在或永远.
为了说明这一点,假设对象定义如下:
class MyClass
{
protected $fillCount = 0;
protected $settings;
public function __construct(array $settings)
{
$this->settings = $settings;
}
public function fill(array $settings)
{
$this->fillCount++;
$this->settings = $settings;
}
}
在这种情况下,您的原始版本和没有 else
的版本最终将具有不同内部状态的对象,并且发现错误会更加困难,因为它会被隐藏假设和隐式结构背后。
现在,让我们来看看一个 PHPMD 自己的 else
:
// File: src/bin/phpmd
if (file_exists(__DIR__ . '/../../../../autoload.php')) {
// phpmd is part of a composer installation
require_once __DIR__ . '/../../../../autoload.php';
} else {
require_once __DIR__ . '/../../vendor/autoload.php';
// PEAR installation workaround
if (strpos('@package_version@', '@package_version') === 0) {
set_include_path(
dirname(__FILE__) . '/../main/php' .
PATH_SEPARATOR .
dirname(__FILE__) . '/../../vendor/pdepend/pdepend/src/main/php' .
PATH_SEPARATOR .
'.'
);
}
}
// (...100+ lines of code follows...)
问题是:我们应该避免这种情况吗else
?
在撰写本文时,为了让 phpmd 满意,只需使用 elseif
而不是 else
(当第一个 if
的正文很长时,在可读性方面有意义)
$condition = ... ;
if ($condition) {
...
} elseif (!$condition) {
...
}