singleton php
PHP: ¿una capa de abstracción de base de datos usa una clase estática contra un objeto singleton? (5)
En mi proyecto más reciente, realmente fui en contra de los "buenos" principios de diseño al hacer que la clase de base de datos fuera completamente estática. La razón detrás de esto es que usé mucho caché en objetos PHP. Originalmente, la base de datos pasó 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, el uso de una base de datos como una variable miembro de ese objeto no hubiera sido práctico porque, si no se serializa un objeto de la memoria caché, no querrá conectarse a la base de datos a menos que realmente realice una operación en él.
Así que al final solo tenía dos funciones estáticas (públicas), Database :: fetch () y Database :: execute () que verificarían si ya se había conectado o no, y si no, se conectaría y realizaría la consulta. De esta manera 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 tienes que seguir todas las buenas prácticas. Pero todavía recomendaría no hacer lo que hice, ya que algunos lo considerarían una optimización prematura.
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, así que espero alguien podría ahora iluminarme respondiendo esta pregunta con uno (o más) EJEMPLOS simples simples , y no solo con discusiones teóricas.
En mi aplicación tengo la clase de base de datos típica para abstraer la capa de base de datos y realizar tareas en la base de datos sin tener que escribir en 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 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, en mi aplicación, si quiero consultar la base de datos que podrí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 podrían darme un EJEMPLO de la ventaja real de singleton en este caso específico . Gracias por adelantado.
Hacer la biblioteca DB estática es ciertamente más corto y más rápido que hacer:
$db = DBSingleton::blabla(); // everytime I need ya
Pero también, ya que es global, tentador de usar en todas partes.
Por lo tanto, elija otros métodos si desea un código limpio ... y elija estática si necesita un código rápido ;-)
Lo que está mal con 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 puede hacer uso de las conexiones de carga lenta y aún tener flexibilidad para usar diferentes conexiones de base 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.
Mi consejo: DEJA DE usar Singleton y estática todos juntos.
¿Por qué? Porque insertará dependencias que harán que su código sea inutilizable en otros proyectos y no permitirá que lo prueben por unidades. También olvide el acoplamiento suelto si usa singleton.
Las alternativas? Inyección de dependencia. http://www.potstuck.com/2009/01/08/php-dependency-injection
/* 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();