Global或Singleton用于数据库连接?

时间:2008-09-25 00:56:28

标签: php design-patterns singleton

在PHP中使用单例而不是全局的数据库连接有什么好处?我觉得使用单例而不是全局会使代码变得不必要地复杂。

全球代码

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

使用Singleton的代码

class DB_Instance
{
    private static $db;

    public static function getDBO()
    {
        if (!self::$db)
            self::$db = new PDO(...);

        return self::$db;
    }
}

function getSomething()
{
    $conn = DB_Instance::getDBO();
    .
    .
    .
}

如果有更好的方法来初始化除全局或单例之外的数据库连接,请提及它并描述它相对于全局或单例的优势。

9 个答案:

答案 0 :(得分:104)

我知道这已经过时了,但Dr8k的回答几乎是

当你考虑编写一段代码时,假设它会改变。这并不意味着你假设将来会在某些时候提出它会发生的变化,而是会做出某种形式的改变。

让目标减轻未来改变的痛苦:全球化是危险的,因为很难在一个地方进行管理。如果我希望将来能够识别数据库连接上下文怎么办?如果我希望它每隔5次关闭并重新打开它,该怎么办?如果我决定为了扩展我的应用程序而想要使用10个连接池,该怎么办?或者可配置数量的连接?

单件工厂为您提供了灵活性。我设置它只需要很少的额外复杂性,并且获得的不仅仅是访问相同的连接;我能够以一种简单的方式改变连接传递给我的方式。

请注意,我说 singleton factory 而不是简单的 singleton 。单身人士与全球人士之间存在着微不足道的差异。因此,没有理由建立一个单独的连接:为什么你会花时间设置那个可以创建常规全局的呢?

工厂得到的是为什么要获得连接,以及一个单独的位置来决定你将获得什么样的连接(或连接)。

实施例

class ConnectionFactory
{
    private static $factory;
    private $db;

    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    public function getConnection() {
        if (!$this->db)
            $this->db = new PDO(...);
        return $this->db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

然后,在6个月内,当你的应用程序超级出名并且得到dugg和slashdotted并且你决定需要多个连接时,你所要做的就是在getConnection()方法中实现一些池化。或者,如果您决定要使用实现SQL日志记录的包装器,则可以传递PDO子类。或者,如果您决定在每次调用时都需要新连接,则可以执行此操作。它很灵活,而不是僵硬。

16行代码,包括大括号,这将为您节省数小时和数小时的重构,使其变得非常类似。

请注意,我不认为这是“功能蠕变”,因为我没有在第一轮中进行任何功能实现。这是边界线“未来的蠕变”,但在某些时候,“为今天的明天编码”的想法总是总是一件好事并不适合我。

答案 1 :(得分:16)

我不确定我是否可以回答您的具体问题,但我想建议全局/单一连接对象可能不是最好的想法,如果这对于基于Web的系统。 DBMS通常用于以有效的方式管理大量唯一连接。如果您正在使用全局连接对象,那么您正在做一些事情:

  1. 强制您的页面执行所有数据库 连续顺序和杀戮 任何异步页面的尝试 负载。

  2. 可能持有开锁 数据库元素长于 必要的,整体放慢 数据库性能。

  3. 最大化总数 同时连接你的 数据库可以支持和阻止 新用户访问 资源。

  4. 我相信还有其他可能的后果。请记住,此方法将尝试为访问该站点的每个用户维持数据库连接。如果您只有一个或两个用户,则不是问题。如果这是一个公共网站,并且您想要流量,那么可扩展性将成为一个问题。

    <强> [编辑]

    在较大的缩放情况下,每次点击数据时创建新连接都可能很糟糕。但是,答案不是创建全局连接并将其重用于所有内容。答案是连接池。

    通过连接池,可以维护许多不同的连接。当应用程序需要连接时,将检索池中的第一个可用连接,然后在其作业完成后将其返回到池中。如果请求连接且没有可用连接,则会发生以下两种情况之一:a)如果未达到允许的最大连接数,则打开新连接,或b)强制应用程序等待连接变为可用

    注意:在.Net语言中,默认情况下,连接池由ADO.Net对象处理(连接字符串设置所有必需的信息)。

    感谢Crad对此发表评论。

答案 2 :(得分:7)

创建单例方法是为了确保只有一个类的实例。但是,因为人们将它用作快捷全球化的一种方式,所以它被称为懒惰和/或糟糕的编程。

因此,我会忽略global和Singleton,因为两者都不是OOP。

您所寻找的是依赖注入

您可以在http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection

查看与依赖注入相关的易于阅读的基于PHP的信息(带示例)

答案 3 :(得分:3)

两种模式都实现了相同的净效果,为数据库调用提供了一个单一的访问点。

就具体实现而言,单例具有一小优势,即在至少一个其他方法请求之前不启动数据库连接。在我编写的大多数应用程序的实践中,这并没有太大的区别,但如果你有一些页面/执行路径根本不进行任何数据库调用,那么这是一个潜在的优势,因为那些页面不会曾要求连接数据库。

另一个小的区别是全局实现可能会无意中践踏应用程序中的其他变量名称。你不可能偶然声明另一个全局$ db引用,尽管你可能会意外地覆盖它(比如,当你想写if($ db == null)时你写if($ db = null)。单例对象可以防止这种情况。

答案 4 :(得分:2)

如果你不打算使用持久连接,并且有些情况没有这样做,我发现单个概念上比OO设计中的全局更可口。

在真正的OO架构中,单例比每次创建对象的新实例更有效。

答案 5 :(得分:2)

在给定的例子中,我认为没有理由使用单身人士。根据经验,如果我唯一关心的是允许对象的单个实例,如果语言允许,我更喜欢使用全局

答案 6 :(得分:1)

一般情况下,我会使用单例进行数据库连接...每次需要与数据库交互时都不想创建新连接...这可能会损害网络的性能和带宽......为什么要创建一个新的,当有一个可用时......只需要我的2美分......

RWendi

答案 7 :(得分:0)

这很简单。切勿使用全局OR Singleton。

答案 8 :(得分:0)

作为建议,单件全局都有效,可以在同一个系统,项目,插件,产品等中加入 .. 。 就我而言,我为网络制作数字产品(插件)。

我在主类中只使用 singleton ,我原则上使用它。我几乎不使用它,因为我知道主类不会再次实例化

<?php // file0.php

final class Main_Class
{
    private static $instance;
    private $time;

    private final function __construct()
    {
        $this->time = 0;
    }
    public final static function getInstance() : self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }
    public final function __clone()
    {
        throw new LogicException("Cloning timer is prohibited");
    }
    public final function __sleep()
    {
        throw new LogicException("Serializing timer is prohibited");
    }
    public final function __wakeup()
    {
        throw new LogicException("UnSerializing timer is prohibited");
    }
}

全局几乎用于所有次要类,例如:

<?php // file1.php
global $YUZO;
$YUZO = new YUZO; // YUZO is name class

在运行时我可以使用全局在同一个实例中调用它们的方法和属性,因为我不需要主产品类的另一个实例。

<?php // file2.php
global $YUZO;
$YUZO->method1()->run();
$YUZO->method2( 'parameter' )->html()->print();

我认为全局是使用相同的实例来使产品工作,因为我不需要同一类实例的工厂,通常实例工厂用于大型系统或非常罕见的目的。

In conclusion:,如果您已经完全理解反模式 Singleton 并了解全局,您必须使用其中一个选项或者混合它们但是如果我建议不要滥用,因为有许多程序员非常例外并且忠实于编程OOP,请将它用于在执行时间内大量使用的主类和次类。 (它为您节省了大量CPU)。