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

为什么人们在PHP框架中使用单例,php,oop,frameworks,dependency-injection,Php,Oop,Frameworks,Dependency Injection,好了,伙计们,我很难理解为什么需要单身 让我们做一个真实的例子:我有一个my CMS的框架 我需要一个类来记录一些信息(让我们继续使用PHP) 例如: class Logger{ private $logs = array(); public function add($log) { $this->logs[]=$log; } } 当然,这个助手对象在我的CMS页面请求的输入期限内必须是唯一的。 为了解决这个问题,我们将它变成一个单例(声明私有

好了,伙计们,我很难理解为什么需要单身

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

例如:

class Logger{
   private $logs = array();

   public function add($log) {
      $this->logs[]=$log;
   }      
}
当然,这个助手对象在我的CMS页面请求的输入期限内必须是唯一的。
为了解决这个问题,我们将它变成一个单例(声明私有构造函数等)

但是为什么这样的类不是完全静态的呢?这将解决singleton模式(被认为是糟糕的实践)示例的需要:

通过使这个助手完全静态,当我们需要在应用程序中的某个地方添加日志时,我们只需要静态地调用它,就像:
Logger::add('log1')vs单例调用,如:
Logger::getInstance()->add('log1')

希望有人能让我更容易理解为什么在PHP中使用单例而不是静态类

编辑
感谢@James,对于感兴趣的人来说,关于单例vs静态类的讲座非常精彩。(请注意,这并不能解决我的问题)

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

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

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

更新:

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

原因很多

静态方法基本上是全局函数,可以从任何范围调用,这会导致难以跟踪错误。您最好根本不使用类

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

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

原因还有很多,但很难一一列举

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

旁注。DI允许您的类不相互依赖,而是依赖于接口。由于它们之间的关系没有巩固,因此以后更容易更改应用程序,并且一个类中断不会同时中断两个类


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

单状态类允许您重写行为。Logger::add('1')仅当Logger类知道如何登录时,才能登录到不同的设备。Logger::getLogger()->add('1')可以执行不同的操作,具体取决于Logger getLogger()返回的子类型。
当然,您可以在logger类中执行所有操作,但通常情况下,您最终会在静态类中实现单例。

我使用单例,因此我可以确切地告诉您为什么要使用单例而不是静态函数

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

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


现在,许多程序员被教导说“单身是不好的,嗯,凯?”但忽略了一点,那就是像这样的东西通常只有在过度使用时才是不好的。就像一个雕刻大师一样,一个经验丰富的程序员有很多工具可供他使用,很多工具不会得到很多的使用。我的数据库处理程序是理想的单例处理程序,但它是我日常使用的唯一一个。对于日志类,我通常使用静态方法。

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

静态类可能有名称空间冲突。您不能加载两个同名的静态类,但可以加载一个单例的两个不同版本,并用相同的名称实例化它们。当我需要测试新版本的类时,我就这样做了。我实例化了该类的不同版本,但不需要更改引用该类的代码


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

我不认为您的示例可以很好地说明我在框架中使用帮助对象的意思。请记住,我指的是PHP,在PHP中,静态对象不在用户之间共享requests@yes123-请求是否共享并不重要,因为网页是非常不确定的,因此您可能有几个请求
class Logger {
    private static $logs = array();

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