【问题标题】:Singleton PHP - database handlerSingleton PHP - 数据库处理程序
【发布时间】:2025-12-04 20:15:02
【问题描述】:

我最近读了一些关于单例模式的文章。在阅读它的技术方面时,它似乎非常适合管理数据库处理程序等。但在阅读了更广泛的资源后,开发者社区似乎真的不赞成这种模式。

我正在努力为此类问题找到更好的解决方案 - 即一次只能初始化一个处理程序 - 那么为什么模式如此糟糕?是过度使用还是根本存在缺陷?

Php 是我使用的语言。

【问题讨论】:

    标签: php design-patterns singleton


    【解决方案1】:

    Singletons are glorified global variables。设计模式是为全局变量很难或不可能的语言创建的,或者它们被认为是不好的做法。 (事实上​​,大多数常见的设计模式都是为限制性语言设计的。其中很多在其他语言中根本不需要。)

    PHP 有全局变量。 PHP 全局变量通常是一种不好的做法,但如果您需要使用它们,它们确实存在。

    但是,您需要 PHP 中的 Singleton 有几个原因。

    当我可能在脚本中的任何位置调用getInstance(返回单例的方法的规范名称)时,单例很有用。在那之前,该对象不需要存在。如果对象是一个全局变量,则要么它必须已经存在,要么尝试引用该对象的代码首先需要实例化它。事实上,在任何可以使用它的地方,都需要正确地实例化它。通过在 getInstance 中集中创建单个对象,您可以避免每次需要引用该对象时都必须创建复制粘贴样板。

    数据库对象通常在请求生命周期的早期就被创建了,这样就浪费了 Singleton-ness 的特定好处。

    Singleton 还有其他替代方案可以通过其他方式完成工作。一个例子是dependency injection,这是一个花哨的术语,用于在构造时将新对象所依赖的外部对象(例如数据库句柄)传递给对象。但是,这可能很复杂或烦人。正确操作可能涉及每次注入很多相同的对象。

    另一种选择是Registry pattern,它实际上是一个容器,可以容纳原本是全局的东西。如果您不喜欢全局变量,但不介意它们被有效命名空间,这将是您想要的解决方案。

    最后,选择一种方法,并在整个代码库中坚持使用这种方法。就我个人而言,我很喜欢数据库对象是一个全局对象。

    【讨论】:

    • 如果你使用一些缓存,可能会发生不需要数据库连接的情况。此外,由于在类上使用了单例,因此可以使用自动加载。我不认为 Singleton 对 PHP 中的数据库处理不利。
    • 非常正确。如果您的代码和数据库之间有一个全面的缓存层,那么您很可能至少在某些时候甚至不需要打开连接。
    • 关于测试的观点非常有效。单例的主要问题是它不仅将您限制为单个实例(两个单独的数据库连接到单独的数据库怎么样?),而且限制为特定的实现。如果你想测试,你必须用其他东西替换你的数据库连接。抽象变得更加困难,因为无法扩展您的单例。注册表解决了其中许多问题。
    • Registry 是一个非常值得的选择,尤其是考虑到有关测试的 cmets。
    • 注册表只是一个单例数组:/ 只需使用依赖注入。
    【解决方案2】:

    有些人认为单例是邪恶的,因为它们就像全局变量,使您的代码更加硬耦合且更难测试。

    我不认为这是一个坏主意,而且我认为 Singleton 非常适合数据库处理程序。它简单、直观,而且您对 Singleton 实例的控制比对全局变量的控制要多。

    【讨论】:

      【解决方案3】:

      单例模式没什么问题,我一直在用。

      正如您所说,它非常适合您应该只有一个实例并且对应用程序是全局的资源。

      我认为一些开发人员不喜欢它,因为可以通过这种方法创建依赖项。阅读依赖注入,因为我认为它涵盖了为什么单例不好,但我记不清了。

      【讨论】:

        【解决方案4】:

        我对大多数较小的 Web 应用程序使用单例数据库处理程序。当与外部配置文件和 PDO 作为数据库访问方法相结合时,它仍然可以非常灵活地从项目转移到项目,只要方法不是特定于应用程序模型的(即 getRow 或 getAll 而不是 getThing 或 getBreakfast) .我只是尝试确保我所有的访问常量都是单独定义的。

        单身人士的许多烦恼主要源于比 PHP 更复杂的语言。

        【讨论】:

        • 我对如何将其与配置文件链接感兴趣
        • @Bill - 我设计的大多数应用程序都倾向于前端控制器模式 oreillynet.com/pub/a/php/2004/07/08/front_controller.html - 所以基本上我可以包含一个 config.php 文件,该文件有许多定义语句来使数据库访问凭据中的常量只要前端控制器加载。然后包括所有模型(数据库处理程序本身),所以我可以在应用程序的任何地方使用该处理程序执行我的数据库查询,因为应用程序的所有部分都从那个入口点下降。
        【解决方案5】:

        除了全局变量之外,社区不喜欢单例,因为最终你可能需要一个额外的类实例。例如,您可以将 Singleton 用于您的数据库连接,并且在您想一次连接到 2 个数据库之前,它工作得很好。此时,您要么重构整个应用程序,要么将单例复制粘贴到新类并使用它。无论哪种方式,您都可以在没有桨的情况下上一条小溪。

        然而,单例模式是一种设计模式,而不是一种实现。没有人说将您限制在一个实例中的代码必须在类中。我发现如果您通过工厂方法实例化该类,您可以将单例实现放在工厂方法中。然后,如果有一天你需要一个新实例,你可以添加一个新的工厂方法来做到这一点,因为类本身没有任何限制。当然,这样做的代价是你总是通过工厂而不是直接实例化你的单例。

        【讨论】: