简体   繁体   English

PHP - 一个DB抽象层使用静态类vs单例对象?

[英]PHP - a DB abstraction layer use static class vs singleton object?

I don't want to create a discussion about singleton better than static or better than global, etc. I read dozens of questions about similar subjects on SO, but I couldn't come up with an answer to this SPECIFIC question, so I hope someone could now illuminate me by answering this question with one (or more) real simple EXAMPLES , and not just theoretical discussions. 我不想创建关于单身人士的讨论比静态或比全球更好等等。我在SO上阅读了关于类似主题的几十个问题,但是我无法想出这个具体问题的答案,所以我希望有人可以用一个(或多个) 真实的简单例子来回答这个问题,而不仅仅是理论上的讨论。

In my app I have the typical DB class to abstract the DB layer and to perform tasks on DB without having to write everywhere in code mysql_connect / mysql_select_db / mysql... 在我的应用程序中,我有一个典型的DB类来抽象数据库层并在数据库上执行任务,而无需在代码mysql_connect / mysql_select_db / mysql...编写任何地方mysql_connect / mysql_select_db / mysql...

I could write the class either as a 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
   }

   ...
}

OR as a SINGLETON: 或作为SINGLETON:

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
   }

   ...
}

Then after in my app if I want to query the DB i could do 然后在我的应用程序后,如果我想查询数据库,我可以做

//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...");

To me Singleton has got the only advantage to avoid declaring as static each method of the class. 对我来说,Singleton有唯一的优势,可以避免将类声明为static每个方法。 I'm sure some of you could give me an EXAMPLE of real advantage of singleton in this specific case . 我敢肯定,你们中的一些可以给我单身的真正优势的例子在这种特殊情况下 Thanks in advance. 提前致谢。

What is wrong with the following (simplified) example: 以下(简化)示例有什么问题:

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

This demonstrates how you can make use of lazy loading connections, and still have flexibility to use different database connections. 这演示了如何使用延迟加载连接,并且仍然可以灵活地使用不同的数据库连接。

The database instances will only connect to their individual connection when an object that consumes one of the instances (in this case one of the models) decides to call a method of the instance. 当使用其中一个实例(在本例中为其中一个模型)的对象决定调用实例的方法时,数据库实例将仅连接到它们各自的连接。

In my most recent project, I actually went against the "good" design principles by making the database class entirely static. 在我最近的项目中,我实际上通过使数据库类完全静态来反对“好”的设计原则。 The reason behind this is that I used a lot of caching on PHP objects. 这背后的原因是我在PHP对象上使用了很多缓存。 Originally I had the database passed in through the constructor of each object as a dependency injection, however I wanted to make sure that the database didn't have to connect unless absolutely necessary. 最初我通过每个对象的构造函数传入数据库作为依赖注入,但是我想确保除非绝对必要,否则数据库不必连接。 Thus, using a database as a member variable of that object would not have been practical because if you unserialized an object from the cache, you wouldn't want to connect to the database unless you actually performed an operation on it. 因此,使用数据库作为该对象的成员变量是不切实际的,因为如果从缓存中反序列化对象,除非您实际对其执行了操作,否则您不希望连接到数据库。

So in the end I had only two (public) static functions, Database::fetch() and Database::execute() which would check whether or not it had already connected, and if not, it would connect and perform the query. 所以最后我只有两个(公共)静态函数,Database :: fetch()和Database :: execute(),它们将检查它是否已经连接,如果没有,它将连接并执行查询。 This way I wouldn't have to worry about deserialization and would connect as seldom as possible. 这样我就不必担心反序列化,并且尽可能少地连接。 It technically makes unit testing impossible though. 它在技术上使单元测试无法实现。

You don't always have to follow every single good practice. 你并不总是要遵循每一个好的做法。 But I would still recommend against doing what I did since some would consider it premature optimization. 但我仍然建议不要做我做的事情,因为有些人认为它过早优化。

My advice: STOP using Singleton and static all together. 我的建议:停止使用Singleton和静态。

Why? 为什么? Because you will insert dependencies that will render your code unusable in other projects, and will not allow to unit test it. 因为您将插入将导致您的代码在其他项目中无法使用的依赖项,并且不允许对其进行单元测试。 Also forget about loose coupling if using singleton. 如果使用单件,也要忘记松耦合。

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

Making DB library static is certainly shorter and quicker, than doing: 使DB库静态肯定更短,更快,而不是:

$db = DBSingleton::blabla(); // everytime I need ya

But also, since it is global, tempting to use everywhere. 但是,因为它是全球性的,很有可能在任何地方使用。

So, choose other methods if you want clean code... and choose static if you need quick code ;-) 所以,如果你想要干净的代码,请选择其他方法......如果需要快速代码,请选择静态;-)

/* 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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM