【问题标题】:PHP Static v Singleton class - ConnectionFactory explainedPHP Static v Singleton 类 - ConnectionFactory 解释
【发布时间】:2012-11-14 15:14:54
【问题描述】:

我是这个级别的 PHP 编程新手,我一直在阅读一篇关于单例和静态类的文章。我正在编写一个有助于我的数据库连接的类。

我遇到了 Jon Raphaelson (here) 编写的以下代码:

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

    private $db;

    public function getConnection() {
        if (!$this->db)                 // this line was modified due to comment
            $this->db = new PDO(...);       // this line was modified due to comment
        return $db;
    }
}

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

我好像找到了我要找的东西,但是我有几个问题。

  1. self::$factory = new ConnectionFactory(...); - 我在这个类中没有看到构造函数。我是否只是创建此构造函数并传入数据库详细信息('dbname'、'user'、'pass'等)?
  2. getSomething() 函数,我假设其目的是将所有将检索数据的实际函数放入 ConnectionFactory 类中 - 这就是该函数位于此类中的原因。否则,我会期望这个函数在另一个类中。 [编辑] 跳过这个问题,我没有看到一个括号。
  3. 当两个用户登录站点并请求数据库连接(都在进行更新等)时会发生什么?这是一个单身人士会有问题吗?

谢谢!

【问题讨论】:

  • 我建议不要使用这种模式。请参阅How Not To Kill Your Testability Using Statics 了解原因和替代方案。
  • @deceze 您是否建议 PHP 中的人不要使用工厂模式或单例模式?我不确定我明白为什么这些本质上是不好的——它们是经过严格审查的 OOP 模式。
  • @Ray 我提倡反对单例模式,更具体地说反对在任何地方静态调用ConnectionFactory::getFactory()。如果您使用的是工厂,则该工厂应作为对象注入,而不是静态调用。单件工厂是毫无意义的。应该使用工厂来为对象 A 提供构造其他对象 B 的方法,而无需将 A 静态耦合到 B。因此,将 A 静态耦合到工厂 B 没有任何优势。
  • @deceze 您将如何强制不创建工厂或连接的多个副本?说资源有限的东西,例如与数据库的连接,您或使用您的代码的人可能会意外创建比可用连接更多的连接。的确,它是更可测试的方法和类,但这样做也更有可能创建单元测试可以愉快地工作的情况,但是当预期的连接被拒绝或由于资源争用而超时时,整个代码会中断。
  • @Ray 将实例化的连接保存为工厂的static 属性。您可以根据需要实例化任意数量的工厂,但它们都只会分配一个连接。更好的是,只在调用链中的某个位置创建一次连接,然后使用依赖注入将其传递给每个需要它的对象。

标签: php static singleton


【解决方案1】:

这主要是为了扩展我的问题下的cmets...

更好的“单例”模式是这样的:

class ConnectionFactory {

    protected static $connection;

    public function getConnection() {
        if (!self::$connection) {
            self::$connection = new PDO(...);
        }
        return self::$connection;
    }

}

这个工厂的用户应该期待它的一个实例,而不是自己调用它:

class Foo {

    protected $connectionFactory;

    public function __construct(ConnectionFactory $factory) {
        $this->connectionFactory = $factory;
    }

    public function somethingThatNeedsAConnection() {
        $connection = $this->connectionFactory->getConnection();
        ...
    }

}

$foo = new Foo(new ConnectionFactory);

这允许Foo 在需要时自行获取连接,但也允许您向Foo 注入一些替代连接,例如用于测试目的。

作为一种便利措施并降低实例化复杂性,这种模式也很好:

class Foo {

    protected $connectionFactory;

    public function __construct(ConnectionFactory $factory = null) {
        if (!$factory) {
            $factory = new ConnectionFactory;
        }
        $this->connectionFactory = $factory;
    }

    ...

}

这仍然允许注入和覆盖依赖项,但它不需要您每次实例化 Foo 时都注入工厂。

【讨论】:

    【解决方案2】:

    工厂是单例的,因为您不想要多个工厂。

    1. 您不需要构造函数,您可以通过 getFactory() 方法为工厂设置所需的任何状态。 getFactory() 函数没有得到连接,所以不要在这里做任何与连接相关的设置,而不是连接对象本身。工厂“构建实例”供您使用,因此您可以使用 getConnection() 方法设置状态并构造工厂应该生成的连接对象。
    2. getSomething() 不是 ConnectionFactory 类中的方法,它只是使用工厂类获取工厂,然后获取连接的示例。

    我个人讨厌 PHP 中的方法链。 getSomething() 中真正发生的事情是:

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

    【讨论】:

    • 雷,谢谢(在 #2 上哎呀,不知道为什么我错过了,也许原来的帖子有不同的间距)。
    • @NEW2WEB 没问题,看我的更新。希望它能回答一切。
    【解决方案3】:
    1. 一个类不需要显式构造函数就可以使用new ClassName() 进行实例化。但是,如果该类应该是一个单例(在这种情况下看起来像),那么该模式的全部意义在于使此类实例化无法从类外部进行,这就是为什么我认为应该使用声明的构造函数private关键字。

    2. get getSomething() 是一个“独立”函数,带有使用您的类的示例。

    3. 没有。多个用户使用多个 PHP 实例处理您的脚本,并且他们之间没有共享任何内容。

    【讨论】:

      猜你喜欢
      • 2011-08-11
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 1970-01-01
      • 2011-09-22
      • 2014-07-31
      • 1970-01-01
      相关资源
      最近更新 更多