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

时间:2010-05-15 16:46:26

标签: php database oop static singleton

我不想创建关于单身人士的讨论比静态或比全球更好等等。我在SO上阅读了关于类似主题的几十个问题,但是我无法想出这个具体问题的答案,所以我希望有人能用一个(或多个)真正简单的例子来回答这个问题,而不仅仅是理论上的讨论。“

在我的应用程序中,我有典型的数据库类来抽象数据库层并在数据库上执行任务,而无需在代码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
   }

   ...
}

或作为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
   }

   ...
}

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

//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声明为类的每个方法。我相信你们中的一些人可以在这个特定情况中给我一个例子真正的单身人士优势。提前谢谢。

5 个答案:

答案 0 :(得分:7)

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

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 :(得分:4)

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

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

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

答案 2 :(得分:4)

我的建议:停止使用Singleton和静态。

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

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

答案 3 :(得分:1)

使DB库静态肯定比执行:

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

但是,因为它是全球性的,很容易在任何地方使用。

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

答案 4 :(得分:-1)

function foodQuery() {

  var key = "123456789";

  $("#searchinput").keyup(function() {
    var value = $(this).val();
    foodQuery(value);
  });

  function foodQuery(key) {    // key is passed as a parameter

  var foodURL = "http://api.example.com/items?key=" + key;

  $.ajax({
    url: foodURL,
    type: 'GET',
    contentType: "text/plain",
    dataType: 'json',
    success: function(json) {
        $.each(json.products, function(index, product) { 
            // build product block
            var htmlString = '<div class="product large-3 columns">';
            //open imgwrap
            htmlString += '<div class="imgwrap">';
            //get img src
            htmlString += ' <img class="item_img" src="http://api.example.com/assets/images/' + product.itemCode + '@2x.jpg" />';
            // close imgwrap
            htmlString += '</div>';
            // open textwrap
            htmlString += '<div class="textwrap">';
            // get productName
            htmlString += '<h1 class="product_headline">' + product.productName + '</h1>' ;
            // get itemCode
            htmlString += '<h4 class="item_id" >#' + product.itemCode + '</h4>';
            // get description
            htmlString += '<p class="product_desc">' + product.description + '</p>';
            // open price
            htmlString += '<div class="price">';
            // get price & close div
            htmlString += '<span class="dollar"><span class="usd">$</span>' + product.price + '</span> <span class="product_desc">per weight</span></div>'
            // close divs
            htmlString += '</div>';

            //console.log(htmlString);
            $('.listing').append( $(htmlString) );

       }); //end each

    }, // end success
    error: function(e) {
       console.log(e.message);
       $('.listing').append( '<h1 class="errmsg" >Sorry, there was an unkown error.</h1>' );
    } // end error
    }); // end ajax request

    $(".listing").html("");
  }
}