PHP - 数据库抽象层使用静态类与单例对象?

2022-08-30 17:52:21

我不想创建一个关于单例的讨论,而不是静态的,或者比全局更好,等等。我读了几十个关于SO类似主题的问题,但我无法想出这个特定问题的答案,所以我希望现在有人可以用一个(或多个)真实简单的例子来回答这个问题,而不仅仅是理论讨论来阐明我。

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

我可以将类编写为静态类:

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


答案 1

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

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 );

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

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


答案 2

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

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

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


推荐