为什么人们在PHP框架中使用单例

时间:2011-05-15 23:57:38

标签: php oop frameworks dependency-injection

好的家伙我很难理解为什么需要单身人士。

让我们举一个真实的例子:我有一个CMS的框架 我需要有一个记录一些信息的类(让我们坚持使用PHP)。

示例:

class Logger{
   private $logs = array();

   public function add($log) {
      $this->logs[]=$log;
   }      
}

当然,对于我的CMS的页面请求的输入生命周期,此帮助程序对象必须是唯一的。
为了解决这个问题,我们将它设为单例(声明私有构造函数等)。

但为什么这样的一个阶级并不是完全静止的呢?这将解决单例模式的需要(这被认为是不好的实践)例如:

class Logger {
    private static $logs = array();

    public static function add($log) {
        self::$logs[]=$log;
    }
}

通过使这个帮助程序完全静态,当我们需要在我们的应用程序中的某处添加日志时,我们只需要静态地调用它:Logger::add('log 1');与单个调用类似:Logger::getInstance()->add('log 1');

希望有人能让我轻松理解为什么在PHP中使用单例而非静态类

修改

感谢@James,

This is a pretty关于单身人士与静态课程的好讲座。 (注意它没有解决我的问题)

5 个答案:

答案 0 :(得分:7)

很多原因。

静态方法基本上是可以从任何范围调用的全局函数,这有助于难以跟踪错误。你可能根本不使用课程。

由于你不能拥有__construct方法,你可能必须在某处放置一个init静态方法。现在,他们的代码中的人不确定先前是否已调用init方法。他们又叫了吗?他们是否必须在代码库中搜索此调用?如果init在某处,但随后被删除或中断怎么办?您的代码中的许多位置现在依赖于调用init方法的位置。

众所周知,静态方法很难通过许多单元测试框架进行单元测试。

还有很多原因,但很难将它们列出来。

如果您使用DI,则不需要单身人士。

旁注。 DI允许您的类不相互依赖,而是依赖于接口。由于他们的关系没有巩固,因此以后更容易更改你的应用程序,并且一个课程打破不会破坏这两个类。

在某些情况下,单个状态类是可行的,例如,如果您的方法都不依赖于其他方法(基本上没有一个方法会改变类的状态)。

答案 1 :(得分:5)

我使用单身人士,所以我可以告诉你为什么我这样做而不是静态功能。

单例的定义特征是它是一个只有一个实例的类。很容易看到“只是一个实例”子句而忘记看“它是一个类”子句。毕竟,它是一个普通的类对象,具有所带来的所有优点。主要是,它有自己的状态,它可以有私有函数(方法)。静态函数必须以更有限或更笨拙的方式进行这两种操作。

也就是说,两者相互补充:可以利用静态函数在同一个类上返回单例。这就是我在最常用的单例中做的事情:数据库处理程序。

现在,许多程序员都被教导说“单身人士很糟糕,不能吗?”但忽略了骑手,在过度使用时,通常情况下通常只会感觉不好。就像一个主雕刻师一样,经验丰富的程序员可以使用大量工具,而且许多工具都不会得到很多用处。我的数据库处理程序非常适合作为单例,但它是我经常使用的 only 。对于日志记录类,我通常使用静态方法。

答案 2 :(得分:2)

单身人士允许您覆盖行为。例如,只有当Logger类知道如何时,Logger :: add('1')才能登录到不同的设备。 Logger :: getLogger() - > add('1')可以执行不同的操作,具体取决于Logger getLogger()返回的子类型。 当然你可以在记录器类中做所有事情,但通常你最终会在静态类中实现单例。

答案 3 :(得分:1)

如果你有一个打开文件的静态方法,写出并关闭它,你最终可能会尝试同时打开同一个文件的两个调用,因为静态方法不能保证有一个实例

但是,如果你使用单例,那么所有调用都使用相同的文件处理程序,所以你总是只有一次写入这个文件。

您可能最终想要排队写入请求,如果有多个,如果您不希望它们失败,或者您必须以其他方式进行同步,但所有调用都将使用相同的实例。< / p>

<强>更新

这可能对PHP中静态与单身的比较有所帮助。

http://moisadoru.wordpress.com/2010/03/02/static-call-versus-singleton-call-in-php/

答案 4 :(得分:1)

正如leblonk所提到的,你不能覆盖静态类,这使得单元测试非常困难。使用单例,您可以实例化“模拟”对象而不是实际的类。无需更改代码。

静态类可能存在名称空间冲突。您不能加载2个相同名称的静态类,但您可以加载2个不同版本的单例并以相同名称实例化它们。当我需要测试新版本的类时,我已经完成了这个。我实例化了该类的不同版本,但不需要更改引用该类的代码。

我经常混合单身和静态。例如,我使用一个数据库类,确保每个主服务器(静态)和从服务器(单例)只有1个连接。 db类的每个实例都可以连接到不同的slave,如果请求连接到同一个slave,则返回singleton对象。主连接是在每个从属单例内实例化的静态对象,因此在所有db实例化对象中只存在1个主连接。