2010-05-15 6 views
22

No quiero crear una discusión sobre Singleton mejor que estática o mejor que global, etc. Leí docenas de preguntas sobre temas similares en SO, pero no pude encontrar una respuesta a esta pregunta ESPECÍFICA, por lo que Espero que alguien pueda ahora iluminarme respondiendo esta pregunta con uno (o más) EJEMPLOS simples reales, y no solo discusiones teóricas.PHP: ¿una capa de abstracción de base de datos utiliza una clase estática frente a un objeto singleton?

En mi aplicación tengo la clase DB típica abstraer la capa DB y para realizar tareas de base de datos sin tener que escribir todas partes en el código mysql_connect/mysql_select_db/mysql...

podría escribir la clase, ya sea como una clase estática:

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 
    } 

    ... 
} 

O como un 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 
    } 

    ... 
} 

Luego, después en mi aplicación si quiero consultar la base de datos que podía hacer

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

Para mí Singleton tiene la única ventaja de evitar declarar como static cada método de la clase. Estoy seguro de que algunos de ustedes me podrían dar un EJEMPLO de ventaja real de singleton en este caso específico . Gracias por adelantado.

+1

El que votó en contra podría por favor deje un comentario y explicar. –

+2

No hay absolutamente ninguna necesidad de hacer que una clase de base de datos sea singleton o se comporte como una clase estática. Me refiero a qué propósito sirve? Si está buscando crear una aplicación que a la vez necesite dos conexiones de bases de datos diferentes, básicamente ya está arruinado. Y la inicialización lenta (de la conexión real) es perfectamente posible con una clase que permite instancias múltiples. –

+0

@fireeyedboy: gracias por su comentario, pero no entiendo exactamente su punto. Al final, ¿crees que es mejor una clase única o estática y por qué? –

Respuesta

7

Lo que está mal en el siguiente ejemplo (simplificado):

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

Esto demuestra cómo se puede hacer uso de conexiones de carga lenta, y aún así tener flexibilidad para utilizar diferentes conexiones de bases de datos.

Las instancias de la base de datos solo se conectarán a su conexión individual cuando un objeto que consume una de las instancias (en este caso uno de los modelos) decide llamar a un método de la instancia.

+0

Lo siento, pero no resuelvo tu ejemplo. 1) no es un singleton, sino una clase genérica, por lo que es obvio que puedes crear múltiples istances para diferentes DBs, eso es lo que una clase genérica debe ser, por el contrario, SIngleon está destinado a devolverte siempre el mismo objeto con el mismo conexión. 2) cuando diga "este ejemplo", incluso la clase estática y las connetcs solo de Singleton cuando sea necesario, la función de inicio simplemente supervisa los parámetros de conexión de DB, la conexión se realiza solo si es necesario durante la llamada a la función de consulta (lea los comentarios en mi código de pregunta). –

+1

Marco, la razón por la que desaconsejo usar un singleton es porque, cuando su aplicación (o una futura) necesita dos conexiones de bases de datos diferentes al mismo tiempo, ya no puede usar esa clase, porque solo puede acomodar una conexión . Además, supongo que usas este singleton para obtener fácilmente la instancia de donde quieras, ¿verdad? Pero eso crea un fuerte acoplamiento. Sin embargo, cuando pasa la conexión de la base de datos al constructor del objeto consumidor, como en mi ejemplo, crea un acoplamiento libre, por lo que puede cambiarlo fácilmente por otra clase con la misma interfaz. –

+0

oh No entendí que en realidad estabas sugiriendo no usar una clase de Singleton o estática, sino usar una clase normal. Ahora entendí tu punto. –

4

En mi proyecto más reciente, en realidad fui en contra de los "buenos" principios de diseño al hacer que la clase de la base de datos sea totalmente estática. La razón detrás de esto es que utilicé mucho almacenamiento en caché en objetos PHP. Originalmente, hice pasar la base de datos a través del constructor de cada objeto como una inyección de dependencia, sin embargo, quería asegurarme de que la base de datos no tuviera que conectarse a menos que fuera absolutamente necesario. Por lo tanto, usar una base de datos como variable miembro de ese objeto no hubiera sido práctico porque si se deserializa un objeto de la memoria caché, no se querría conectar a la base de datos a menos que realmente se realice una operación.

Así que al final solo tenía dos funciones estáticas (públicas), Database :: fetch() y Database :: execute() que verificaban si ya se habían conectado, y si no, se conectarían y realizar la consulta. De esta forma no tendría que preocuparme por la deserialización y me conectaría lo menos posible. Sin embargo, técnicamente hace que las pruebas unitarias sean imposibles.

No siempre tiene que seguir todas las buenas prácticas. Pero aún así recomendaría no hacer lo que hice, ya que algunos lo considerarían una optimización prematura.

+0

Yo diría que crear un SINGLETON es una optimización prematura, ya que cerar una clase estática es muy simple. –

3

Mi consejo: DEJA de usar Singleton y estático todos juntos.

¿Por qué?Debido a que insertará dependencias que harán que su código no se pueda usar en otros proyectos, y no permitirá que la unidad lo pruebe. También olvide el acoplamiento flojo si usa singleton.

¿Las alternativas? Inyección de dependencia. http://www.potstuck.com/2009/01/08/php-dependency-injection

+0

PHP evite las clases estáticas para evitar dependencias, pero entonces necesito usar global en todas partes: http://stackoverflow.com/questions/10486107/php-do-not-use-static-classes-to-avoid-dependencies-but- then-i-need-to-use-glob –

+0

De hecho: ahora es casi 2015 e incluso los frameworks más grandes y básicamente todas las bibliotecas ORM usan métodos estáticos. – Sliq

1

Haciendo DB estática biblioteca es ciertamente más corto y más rápido, que no hacer:

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

Pero también, ya que es global, la tentación de utilizar todas partes.

lo tanto, elegir otros métodos si desea código limpio ... y elija estática si necesidad código rápido ;-)

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