3

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

else 从来都不是必需的,您可以在这部分代码上简化代码以使其在没有 else 的情况下工作:

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

return $settings;

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

任何想法?

4

3 回答 3

5

可能是因为它可以重写为

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

return $settings;

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

于 2017-05-21T18:42:43.613 回答
4

总长,博士

  • PHPMD有一点
  • 意图是好的,但 PHPMD 的else“从来没有必要”的建议是错误的
  • 你总是可以删除else——但很多时候这会导致代码更混乱(这很讽刺,因为你会遵循“混乱检测器”的建议)。
  • 所以你应该经常问自己:我应该避免这种情况else吗?
  • 甚至 PHPMD 本身也使用else,所以看起来有时它是必要的

长答案

else是一种非常标准的语言结构,用于许多语言和软件包(包括 PHPMD 本身)。对我来说,说那else从来没有必要与说那从来没有必要是一样的,do...while因为你可以结合while (true)and 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));

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

恕我直言,它是。我总是在这样的情况下使用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),我们可以else在这样的代码中删除和提高代码的清晰度/可维护性,而不会丢失任何内容:


    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));
    }

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


    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));
    }

我不确定你,但第一个版本更接近我的计算思路,因此比第二个版本更容易理解和维护,即使它使用了“禁止” 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上述内容的版本中,编程逻辑会发生细微的变化:您(和所有未来的开发人员)必须假设使用我的设置调用 $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吗?

于 2019-05-10T18:39:07.403 回答
-1

在撰写本文时,为了让 phpmd 满意,只需使用elseif而不是(当第一个的主体很长else时,在可读性方面有意义)if

$condition = ... ;
if ($condition) {
   ...
} elseif (!$condition) {
   ...
}
于 2020-03-16T23:48:45.720 回答