关于oop:为什么人们在他们的PHP框架中使用单例

Why people use singletons in their PHP framework

好吧,伙计们,我正在努力理解为什么需要单身。

让我们做一个真正的例子:我有一个我的CMS框架我需要一个记录一些信息的类(让我们继续使用PHP)。

例子:

1
2
3
4
5
6
7
class Logger{
   private $logs = array();

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

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

但是,为什么像这样的类在地狱里不是完全静态的呢?这将解决单例模式(这被认为是不好的实践)的需求示例:

1
2
3
4
5
6
7
class Logger {
    private static $logs = array();

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

通过使这个助手完全静态化,当我们需要在应用程序中的某个地方添加一个日志时,我们只需要静态地调用它,就像:Logger::add('log 1');和singleton调用,就像:Logger::getInstance()->add('log 1');一样。

希望有人能让我更容易理解为什么在PHP中对静态类使用singleton。

编辑

多亏了@james,这是一个非常好的关于单件vs静态课程的讲座,对于有兴趣的人来说。(注意,它没有解决我的问题)


原因很多。

静态方法基本上是全局函数,可以从任何范围调用,这使得自己很难跟踪错误。你最好不要使用类。

因为您不能有一个构造方法,所以您可能需要在某个地方放置一个init静态方法。现在代码中的人不确定init方法以前是否被调用过。他们又叫它了吗?他们必须搜索这个调用的代码库吗?如果init在某个地方,但随后被删除或中断怎么办?代码中的许多地方现在都依赖于调用init方法的地方。

静态方法是众所周知的,在许多单元测试框架中很难进行单元测试。

还有很多原因,但很难列出所有原因。

如果您使用的是DI,那么也不需要单例。

侧记DI允许类不相互依赖,而是依赖接口。由于它们的关系没有被巩固,以后更容易更改应用程序,并且一次类中断不会同时中断这两个类。

在某些情况下,单状态类是可行的,例如,如果没有任何方法依赖于其他方法(基本上没有任何方法更改类的状态)。


我使用单例函数,所以我可以确切地告诉你为什么我要使用它而不是静态函数。

单例的定义特征是它是一个只有一个实例的类。很容易看到"只有一个实例"子句,而忘记看到"它是一个类"子句。毕竟,它是一个具有所有优点的普通类对象。原则上,它有自己的状态,可以有私有的功能(方法)。静态函数必须以更有限或更笨拙的方式来完成这两个任务。

也就是说,这两个函数是相辅相成的:可以利用静态函数在同一个类上返回一个单例。这就是我在singleton中最经常使用的方法:数据库处理程序。

现在,许多程序员被教导"单件是坏的,嗯?"但是忽视了骑手,像这样的东西通常只有在过度使用时才是坏的。就像一个大师级的雕刻家一样,一个有经验的程序员有很多工具可供他使用,而且很多工具不会得到很多使用。我的数据库处理程序是单例的理想选择,但它是我日常使用的唯一一个。对于日志类,我通常使用静态方法。


单例允许您重写行为。logger::add("1")例如,只有logger类知道如何才能登录到不同的设备。logger::getlogger()->add("1")可以根据logger getlogger()返回的子类型执行不同的操作。当然,您可以在logger类中完成所有工作,但通常您最终会在静态类中实现单例。


正如leblonk提到的,您不能重写静态类,这使得单元测试非常困难。使用单例,您可以实例化一个"模拟"对象,而不是实际的类。不需要更改代码。

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

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


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

但是,如果您使用单例,那么所有调用都使用相同的文件处理程序,因此您每次只能对该文件进行一次写入。

如果有几个写请求,如果您不希望它们失败,或者您必须以其他方式进行同步,那么您最终可能会希望将这些写请求排队,但所有调用都将使用相同的实例。

更新:

在PHP中,比较静态与单例可能会有所帮助。

Static call versus Singleton call in PHP