好吧,我正在努力理解为什么需要单身人士。
让我们举一个真实的例子:我有一个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,有关单身人士与静态课程的好评,感兴趣的是谁。 (注意它没有解决我的问题)
很多原因。
静态方法基本上是可以从任何范围调用的全局函数,这有助于难以跟踪错误。你也可以根本不使用课程。
由于您不能使用__construct方法,因此可能必须在某处放置init静态方法。现在,他们的代码中的人不确定先前是否已调用init方法。他们又叫了吗?他们是否必须在代码库中搜索此调用?如果init在某处,但随后被删除或中断怎么办?您的代码中的许多位置现在依赖于调用init方法的位置。
众所周知,静态方法很难通过许多单元测试框架进行单元测试。
还有很多原因,但很难将它们列出来。
如果您使用DI,则不需要单身人士。
旁注。 DI允许您的类不相互依赖,而是依赖于接口。由于他们的关系没有巩固,因此以后更容易更改您的应用程序,并且一个课程中断不会破坏这两个类。
在某些情况下,单个状态类是可行的,例如,如果您的方法都不依赖于其他方法(基本上没有一个方法会改变类的状态)。
我使用单身,所以我可以告诉你为什么我这样做而不是静态功能。
单例的定义特征是它是一个只有一个实例的类。很容易看到“只是一个实例”子句而忘记看“它是一个类”子句。毕竟,它是一个普通的类对象,具有所带来的所有优点。主要是,它有自己的状态,它可以有私有函数(方法)。静态函数必须以更有限或更笨拙的方式执行这两种操作。
也就是说,两者相辅相成:可以利用静态函数在同一个类上返回单例。这就是我在最常用的单例中做的事情:数据库处理程序。
现在,许多程序员都被教导说“单身人士很糟糕,不能吗?”但忽视骑手,事情往往只是坏事 什么时候过度使用。就像一位高手雕刻师一样,经验丰富的程序员可以使用大量工具,而且许多工具都不会得到很多用处。我的数据库处理程序非常适合作为单例,但它是 只要 一个我经常使用的。对于日志记录类,我通常使用静态方法。
单身人士允许您覆盖行为。例如,只有当Logger类知道如何时,Logger :: add('1')才能登录到不同的设备。 Logger :: getLogger() - > add('1')可以执行不同的操作,具体取决于Logger getLogger()返回的子类型。
当然,您可以在记录器类中执行所有操作,但通常最终会在静态类中实现单例。
如果你有一个打开文件的静态方法,写出并关闭它,你最终可能会尝试同时打开同一个文件的两个调用,因为静态方法不能保证有一个实例。
但是,如果您使用单例,则所有调用都使用相同的文件处理程序,因此您始终只能一次写入此文件。
您可能最终想要排队写请求,如果有多个,如果您不希望它们失败,或者您必须以其他方式同步,但所有调用将使用相同的实例。
更新:
在PHP中,这可能有助于静态与单例的比较。
http://moisadoru.wordpress.com/2010/03/02/static-call-versus-singleton-call-in-php/
正如leblonk所提到的,你不能覆盖静态类,这使得单元测试非常困难。使用单例,您可以实例化“模拟”对象而不是实际类。无需更改代码。
静态类可能存在名称空间冲突。您不能加载2个相同名称的静态类,但您可以加载2个不同版本的单例并以相同名称实例化它们。当我需要测试新版本的类时,我已经这样做了。我实例化了该类的不同版本,但不需要更改引用该类的代码。
我经常混合单身和静态。例如,我使用一个数据库类,确保每个主服务器(静态)和从服务器(单例)只有1个连接。 db类的每个实例都可以连接到不同的slave,如果请求连接到同一个slave,则返回singleton对象。主连接是在每个从属单例内实例化的静态对象,因此在所有db实例化对象中只存在1个主连接。