【问题标题】:PHP - a DB abstraction layer use static class vs singleton object?PHP - 数据库抽象层使用静态类与单例对象?
【发布时间】:2011-02-19 22:04:19
【问题描述】:

我不想就单例比静态更好或比全局更好等进行讨论。我在 SO 上阅读了许多关于类似主题的问题,但我无法回答这个具体问题,所以我希望现在有人可以通过一个(或多个)真正简单的示例回答这个问题来启发我,而不仅仅是理论上的讨论。

在我的应用程序中,我有典型的 DB 类来抽象 DB 层并在 DB 上执行任务,而无需在代码中到处写 mysql_connect / mysql_select_db / mysql...

我可以将这个类写成 STATIC CLASS:

class DB
{
   private static $connection = FALSE; //connection to be opened

   //DB connection values
   private static $server = NULL; private static $usr = NULL; private static $psw = NULL; private static $name = NULL;

   public static function init($db_server, $db_usr, $db_psw, $db_name)
   {
      //simply stores connections values, without opening connection
   }

   public static function query($query_string)
   {
      //performs query over alerady opened connection, if not open, it opens connection 1st
   }

   ...
}

或作为单身人士:

class DBSingleton
{
   private $inst = NULL;
   private $connection = FALSE; //connection to be opened

   //DB connection values
   private $server = NULL; private $usr = NULL; private $psw = NULL; private $name = NULL;

   public static function getInstance($db_server, $db_usr, $db_psw, $db_name)
   {
      //simply stores connections values, without opening connection

      if($inst === NULL)
         $this->inst = new DBSingleton();
      return $this->inst;
   }
   private __construct()...

   public function query($query_string)
   {
      //performs query over already opened connection, if connection is not open, it opens connection 1st
   }

   ...
}

然后在我的应用程序中,如果我想查询我可以做的数据库

//Performing query using static DB object
DB:init(HOST, USR, PSW, DB_NAME);
DB::query("SELECT...");

//Performing query using DB singleton
$temp = DBSingleton::getInstance(HOST, USR, PSW, DB_NAME);
$temp->query("SELECT...");

对我来说,Singleton 的唯一优势是避免将类的每个方法声明为 static。我相信你们中的一些人可以给我一个示例,说明在这个特定情况中单例的真正优势。提前致谢。

【问题讨论】:

  • 投了反对票的可以留言解释一下。
  • 绝对没有必要将数据库类设为单例或表现为静态类。我的意思是它有什么用途?如果您正在寻找同时需要两个不同数据库连接的应用程序,那么您基本上已经搞砸了。允许多个实例的类完全可以延迟初始化(实际连接)。
  • @fireeyedboy:感谢您的评论,但我不完全理解您的意思。最后,您认为单例类还是静态类更好,为什么?
  • 以上都不是。 :) 我强烈建议不要使用这两个选项中的任何一个。为什么你觉得这两个选项之一是必要的?
  • 请参阅我的回答,了解如何实现延迟加载连接并保持灵活性。

标签: php database oop static singleton


【解决方案1】:

在我最近的项目中,我实际上违背了“好的”设计原则,使数据库类完全静态。这背后的原因是我在 PHP 对象上使用了很多缓存。最初我通过每个对象的构造函数将数据库作为依赖注入传入,但是我想确保数据库不必连接,除非绝对必要。因此,使用数据库作为该对象的成员变量是不切实际的,因为如果您从缓存中反序列化一个对象,除非您实际对其执行操作,否则您不会想要连接到数据库。

所以最后我只有两个(公共)静态函数,Database::fetch() 和 Database::execute(),它们将检查它是否已经连接,如果没有,它将连接并执行查询。这样我就不必担心反序列化并且尽可能少地连接。但从技术上讲,它使单元测试变得不可能。

您不必总是遵循每一个好的做法。但我仍然建议不要做我所做的事情,因为有些人会认为这是过早的优化。

【讨论】:

  • 我会说创建一个 SINGLETON 是过早的优化,因为创建一个静态类非常简单。
【解决方案2】:

以下(简化的)示例有什么问题:

class Database
{
    protected $_connection;

    protected $_config;

    public function __construct( array $config ) // or other means of passing config vars
    {
        $this->_config = $config;
    }

    public function query( $query )
    {
        // use lazy loading getter
        return $this->_getConnection()->query( $query );
    }

    protected function _getConnection()
    {
        // lazy load connection
        if( $this->_connection === null )
        {
            $dsn = /* create valid dsn string from $this->_config */;

            try
            {
                $this->_connection = new PDO( $dsn, $this->_config[ 'username' ], $this->_config[ 'password' ] );
            }
            catch( PDOException $e )
            {
                /* handle failed connecting */
            }
        }

        return $this->_connection;
    }
}

$db1 = new Database( array(
    'driver'   => 'mysql',
    'host'     => 'localhost',
    'dbname'   => 'test',
    'username' => 'test_root',
    'password' => '**********'
) );

$db2 = new Database( array(
    'driver'   => 'pgsql',
    'host'     => '213.222.1.43',
    'dbname'   => 'otherdb',
    'username' => 'otherdb_root',
    'password' => '**********'
) );

$someModel       = new SomeModel( $db1 );
$someOtherModel  = new SomeOtherModel( $db2 );
$yetAnotherModel = new YetAnotherModel( $db2 );

这演示了如何利用延迟加载连接,并且仍然可以灵活地使用不同的数据库连接。

仅当使用其中一个实例(在本例中为模型之一)的对象决定调用实例的方法时,数据库实例才会连接到它们各自的连接。

【讨论】:

  • 对不起,我不明白你的例子。 1)它不是一个单例,而是一个通用类,所以很明显,您可以为不同的数据库创建多个实例,这就是通用类的含义,相反,SIngleon 旨在始终为您返回具有相同对象的相同对象联系。 2)当你说“这个例子”时,即使是静态类和 Singleton 也只在需要时连接,init 函数只是简单地存储 DB 连接参数,只有在调用查询函数时才执行连接(读取 cmets in我的问题代码)。
  • Marco,我不建议使用单例的原因是,当您的应用程序(或未来的应用程序)同时需要两个不同的数据库连接时,您不能再使用该类了,因为它只能容纳一个连接。此外,我认为您使用这个单例可以轻松地从您喜欢的任何地方获取实例,对吧?但这会产生强耦合。但是,当您将数据库连接传递给消费对象的构造函数时,就像在我的示例中一样,您创建了松散耦合,因此您可以轻松地将其交换为具有相同接口的另一个类。
  • 哦,我不明白您实际上是在建议不要使用 Singleton 或静态类,而是使用普通类。现在我明白你的意思了。
  • @Marco:没问题。希望它是有用的信息,以及对您有用的示例。无论如何,我强烈建议您选择这条路线。
  • @Panique:你问过你的同事为什么他们说每次都通过$db var 是不好的吗?而且我想你不明白fireeyedboy的回答,他不是每次都建议传递$db var,他是建议将DB类创建为普通类,(没有singleteon/static),这样你可以创建多个实例db 类连接到多个数据库。
【解决方案3】:

我的建议:停止同时使用 Singleton 和 static。

为什么?因为您将插入会使您的代码在其他项目中无法使用的依赖项,并且不允许对其进行单元测试。如果使用单例,也要忘记松散耦合。

替代方案?依赖注入。 http://www.potstuck.com/2009/01/08/php-dependency-injection

【讨论】:

  • PHP 避免使用静态类以避免依赖,但是我需要在任何地方使用全局:stackoverflow.com/questions/10486107/…
  • 附带事实:现在快 2015 年了,即使是最大的框架和基本上所有的 ORM 库都使用静态方法。
【解决方案4】:

将数据库库设为静态肯定比这样做更短、更快:

$db = DBSingleton::blabla(); // 每次我需要你时

而且,由于它是全球性的,因此很容易在任何地方使用。

因此,如果您想要干净的代码,请选择其他方法...如果您需要快速代码,请选择静态 ;-)

【讨论】:

    【解决方案5】:
    /* Data base*/
     class Database
    {
        /* Database field definition */
        private static $_instance; /instance
        private $_connection;
        private $DB_USER = "database_user_name_here";
        private $DB_PASS = "your_password_here";
        private $DB_NAME = "your_database_name_here";
        private $DB_SERVER = "localhost";
    
        /* Initiate the database connection */
        private function __construct()
        {
            $this->_connection = new mysqli($this->DB_SERVER ,
                                            $this->DB_USER ,
                                            $this->DB_PASS ,
                                            $this->DB_NAME);
            /* Test if connection succeeded */
            if (mysqli_connect_errno()) {
                die("Database connection failed: " .
                    mysqli_connect_error() .
                    " (" . mysqli_connect_errno() . ")"
                );
            }
        }
    
        /**
         * Instance of the database
         * @return Database
         *
         */
        public static function Instance()
        {
            if (!self::$_instance) { // If no instance then make one
                self::$_instance = new self();
            }
    
            return self::$_instance;
        }
    
        /**
         * Void duplicate connection
         */
        private function __clone() { }
    
        /* Return a connection */
        public function getConnection()
        {
            return $this->_connection;
        }
    
    }
    
    /** This is how you would use it in a different class.
      @var TYPE_NAME $connection */
    $db = Database::Instance();
    $connection = $db->getConnection();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-14
      • 1970-01-01
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 2020-08-06
      • 1970-01-01
      • 2011-04-19
      相关资源
      最近更新 更多