【发布时间】:2025-12-04 20:15:02
【问题描述】:
我最近读了一些关于单例模式的文章。在阅读它的技术方面时,它似乎非常适合管理数据库处理程序等。但在阅读了更广泛的资源后,开发者社区似乎真的不赞成这种模式。
我正在努力为此类问题找到更好的解决方案 - 即一次只能初始化一个处理程序 - 那么为什么模式如此糟糕?是过度使用还是根本存在缺陷?
Php 是我使用的语言。
【问题讨论】:
标签: php design-patterns singleton
我最近读了一些关于单例模式的文章。在阅读它的技术方面时,它似乎非常适合管理数据库处理程序等。但在阅读了更广泛的资源后,开发者社区似乎真的不赞成这种模式。
我正在努力为此类问题找到更好的解决方案 - 即一次只能初始化一个处理程序 - 那么为什么模式如此糟糕?是过度使用还是根本存在缺陷?
Php 是我使用的语言。
【问题讨论】:
标签: php design-patterns singleton
Singletons are glorified global variables。设计模式是为全局变量很难或不可能的语言创建的,或者它们被认为是不好的做法。 (事实上,大多数常见的设计模式都是为限制性语言设计的。其中很多在其他语言中根本不需要。)
PHP 有全局变量。 PHP 全局变量通常是一种不好的做法,但如果您需要使用它们,它们确实存在。
但是,您需要 PHP 中的 Singleton 有几个原因。
当我可能在脚本中的任何位置调用getInstance(返回单例的方法的规范名称)时,单例很有用。在那之前,该对象不需要存在。如果对象是一个全局变量,则要么它必须已经存在,要么尝试引用该对象的代码首先需要实例化它。事实上,在任何可以使用它的地方,都需要正确地实例化它。通过在 getInstance 中集中创建单个对象,您可以避免每次需要引用该对象时都必须创建复制粘贴样板。
数据库对象通常在请求生命周期的早期就被创建了,这样就浪费了 Singleton-ness 的特定好处。
Singleton 还有其他替代方案可以通过其他方式完成工作。一个例子是dependency injection,这是一个花哨的术语,用于在构造时将新对象所依赖的外部对象(例如数据库句柄)传递给对象。但是,这可能很复杂或烦人。正确操作可能涉及每次注入很多相同的对象。
另一种选择是Registry pattern,它实际上是一个容器,可以容纳原本是全局的东西。如果您不喜欢全局变量,但不介意它们被有效命名空间,这将是您想要的解决方案。
最后,选择一种方法,并在整个代码库中坚持使用这种方法。就我个人而言,我很喜欢数据库对象是一个全局对象。
【讨论】:
有些人认为单例是邪恶的,因为它们就像全局变量,使您的代码更加硬耦合且更难测试。
我不认为这是一个坏主意,而且我认为 Singleton 非常适合数据库处理程序。它简单、直观,而且您对 Singleton 实例的控制比对全局变量的控制要多。
【讨论】:
单例模式没什么问题,我一直在用。
正如您所说,它非常适合您应该只有一个实例并且对应用程序是全局的资源。
我认为一些开发人员不喜欢它,因为可以通过这种方法创建依赖项。阅读依赖注入,因为我认为它涵盖了为什么单例不好,但我记不清了。
【讨论】:
我对大多数较小的 Web 应用程序使用单例数据库处理程序。当与外部配置文件和 PDO 作为数据库访问方法相结合时,它仍然可以非常灵活地从项目转移到项目,只要方法不是特定于应用程序模型的(即 getRow 或 getAll 而不是 getThing 或 getBreakfast) .我只是尝试确保我所有的访问常量都是单独定义的。
单身人士的许多烦恼主要源于比 PHP 更复杂的语言。
【讨论】:
除了全局变量之外,社区不喜欢单例,因为最终你可能需要一个额外的类实例。例如,您可以将 Singleton 用于您的数据库连接,并且在您想一次连接到 2 个数据库之前,它工作得很好。此时,您要么重构整个应用程序,要么将单例复制粘贴到新类并使用它。无论哪种方式,您都可以在没有桨的情况下上一条小溪。
然而,单例模式是一种设计模式,而不是一种实现。没有人说将您限制在一个实例中的代码必须在类中。我发现如果您通过工厂方法实例化该类,您可以将单例实现放在工厂方法中。然后,如果有一天你需要一个新实例,你可以添加一个新的工厂方法来做到这一点,因为类本身没有任何限制。当然,这样做的代价是你总是通过工厂而不是直接实例化你的单例。
【讨论】: