静态方法:考虑到 PHP 5.3 后期静态绑定,它们仍然很糟糕吗?


Static methods: are they still bad considering PHP 5.3 late static binding?

如果你搜索静态方法不好的原因,你会发现的第一件事是因为你在单元测试时无法覆盖它。

那么考虑到在 PHP 5.3 中您可以通过引入 static:: 做任何您想做的事情,这仍然是正确的吗?

加:

http://sebastian-bergmann.de/archives/883-Stubbing-and-Mocking-Static-Methods.html

请注意,他甚至解释了如何在没有任何测试问题的情况下使用单例:

  • http://sebastian-bergmann.de/archives/882-Testing-Code-That-Uses-Singletons.html

如果你有一个静态成员函数,它通常可以是一个自由函数。通常的反应是,编码人员选择静态成员函数只是因为"一切都必须在对象中"的神话。

这就是为什么人们不鼓励他们。

而且,因为这不是一个非常有说服力的论点,所以这些人指出了单元测试。不知道他们现在会做什么。

静态方法本身并不坏。有时,某些操作对于要求特定对象执行的操作没有意义。例如,像平方根这样的函数作为静态函数会更有意义。

Math::sqrRoot(5);

而不必实例化一个 Math "对象",然后调用该函数。

$math = new Math();
$result = $math->sqrRoot(5);

更难测试是一个原因,但不是唯一的原因。静态方法提供全局访问,全局访问是不好的。

当然,您会发现还有另一种对对象的全局访问,那就是创建一个具有"new"的对象。对象必须在某处创建,因此我们无法消除它(尽管最小化它是一个好主意)。静态方法作为对类的全局访问是不好的,除非它通过更高级的编程来替换"new":

$user = new User();
$user->setPointsTo(100);
// vs
$user = User::with100StartingPoints();

在这种情况下,我创建了更具可读性的代码,同时又不滥用全局访问(无论如何都需要调用"new")。

编辑:让我给你一个例子,说明静态方法"可以"成为可测试性的死亡(注意在上面的例子中,你甚至不需要模拟静态方法,但可以很容易地测试新的静态方法产生相同的结果)。让我们使用您的记录器示例:

class Logger {
    public static function log($text) { // etc }
}
class AccessControl {
    public function isAllowed(User $user, $action) {
      // do stuff, determine if $allowed
      if (!$allowed) {
          Logger::log('user X was not allowed to do Y');
      }
      // do stuff
    }
}

由于对 Logger::log 的全局调用,我们无法干净地测试此方法。这将取决于记录器类的正确工作。