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.
我不想讨论创建一个单例比静态或比全球,等等。我读了许多关于类似的主题,但我不能想出这个特定问题的答案,所以我希望有人可以照亮我的回答这个问题与一个(或多个)真正的简单的例子,而不只是理论的讨论。
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类来抽象DB层,并在DB上执行任务,而不必在代码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:
或作为一个单例:
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
然后在我的app中,如果我想查询DB我可以做
//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. I'm sure some of you could give me an EXAMPLE of real advantage of singleton in this specific case. Thanks in advance.
对我来说,单例模式是唯一的优势,可以避免声明为类的静态方法。我相信你们中的一些人可以给我一个例子,说明在这个特定的情况下单例的真正优势。提前谢谢。
5 个解决方案
#1
7
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.
当一个对象使用一个实例(在本例中是其中一个模型)决定调用实例的方法时,数据库实例将只连接到它们的单个连接。
#2
4
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. 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.
在我最近的项目中,我实际上违背了“良好”的设计原则,使数据库类完全静态。这背后的原因是我在PHP对象上使用了大量的缓存。最初,我将数据库作为依赖项注入通过每个对象的构造函数传入,但是我希望确保数据库在绝对必要的情况下不需要连接。因此,将数据库作为该对象的成员变量使用是不现实的,因为如果从缓存中反序列化一个对象,除非您实际对其执行操作,否则您不会希望连接到数据库。
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. This way I wouldn't have to worry about deserialization and would connect as seldom as possible. It technically makes unit testing impossible though.
所以最后我只有两个(公共的)静态函数:Database: fetch()和Database::execute(),它们将检查它是否已经连接,如果没有,它将连接并执行查询。这样,我就不必担心反序列化,并且尽可能少地连接。但是从技术上讲,它使单元测试变得不可能。
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.
你不必总是遵循每一个好的实践。但是我仍然建议不要做我所做的事情,因为有些人会认为这是不成熟的优化。
#3
3
My advice: STOP using Singleton and static all together.
我的建议是:停止使用单例和静态。
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
#4
1
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 ;-)
所以,如果你想要干净的代码,选择其他的方法……如果你需要快速代码,选择静态;
#5
-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();
#1
7
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.
当一个对象使用一个实例(在本例中是其中一个模型)决定调用实例的方法时,数据库实例将只连接到它们的单个连接。
#2
4
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. 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.
在我最近的项目中,我实际上违背了“良好”的设计原则,使数据库类完全静态。这背后的原因是我在PHP对象上使用了大量的缓存。最初,我将数据库作为依赖项注入通过每个对象的构造函数传入,但是我希望确保数据库在绝对必要的情况下不需要连接。因此,将数据库作为该对象的成员变量使用是不现实的,因为如果从缓存中反序列化一个对象,除非您实际对其执行操作,否则您不会希望连接到数据库。
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. This way I wouldn't have to worry about deserialization and would connect as seldom as possible. It technically makes unit testing impossible though.
所以最后我只有两个(公共的)静态函数:Database: fetch()和Database::execute(),它们将检查它是否已经连接,如果没有,它将连接并执行查询。这样,我就不必担心反序列化,并且尽可能少地连接。但是从技术上讲,它使单元测试变得不可能。
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.
你不必总是遵循每一个好的实践。但是我仍然建议不要做我所做的事情,因为有些人会认为这是不成熟的优化。
#3
3
My advice: STOP using Singleton and static all together.
我的建议是:停止使用单例和静态。
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
#4
1
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 ;-)
所以,如果你想要干净的代码,选择其他的方法……如果你需要快速代码,选择静态;
#5
-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();