缘起
工作中写 API 接口, 需要连接数据库, 因为 MySQL 的连接资源是有限的, 所以为了避免因为反复连接数据库造成性能和资源的浪费, 所以考虑选择使用"单例模式"连接数据库.
"单例模式"的概念
"单例模式 / Singleton Pattern" 表示这个类只能造出一个对象. 如果用来连接数据库, 就能保证有且只连接一次数据库, 避免反复连接.
"单例模式"的特点及其逻辑推导
"单例模式"的主要特点是“三私一公二静态”:
(这里为了让总结更简短, 把权限 private
和 protected
(即"非 public "的)统称为"私", 把权限 public
称为"公")
- "构造函数"必须声明为私有的或者受保护的
- "克隆函数"必须声明为私有的或者受保护的
-
私有的或者受保护的静态的"变量"(通常命名为
$_instance
) - 提供一个访问这个实例的公共的静态的方法(通常命名为
getInstance()
)
“三私一公二静态”逻辑推导:
- 需要一个保存类的唯一实例的变量, 而且为了保证让这个类只有一个实例,该变量必须是一个静态变量 (
static
), 而且这个代表实例的变量只可以被后面的实例化方法getInstance()
使用, 不可被外部访问, 设置为private
; - 需要提供一个访问这个实例的一个静态方法(通常命名为
getInstance()
方法), 从而返回唯一实例的一个引用, 避免了实例化出很多对象; 因为外部需要通过该方法来调用, 所以必须是"公共" (public
) 的; - 为了防止类的外部
new
一个对象从而失去单例的意义(即, 避免这个类自身的构造函数创建对象),需要将"构造函数"设置成protected
或者private
, 从而限制外部实例化出新对象; - 同样的, 为了避免外部通过克隆创建对象, 需要将"克隆函数"设置成
protected
或者private
; - 通过这些设置, 外部就只能通过唯一的接口 --
getInstance()
方法来创建一个静态的 "Singleton 类"
"单例模式"的经典代码
class Single{
private static $_instance;
private function __construct(){
}
private function __clone(){
}
public static function getInstance(){
if(!(self::$_instance instanceof Single)){
self::$_instance = new self();
}
return self::$_instance ;
}
}
检验"单例模式"的效果
为了检验是否"有且只能生成一个对象", 可以在构造函数中生成随机数, 然后检验两次实例产生的随机数是否相同:
class Single{
private static $_instance;
private function __construct(){
$this->rand = mt_rand(1000, 9999);
}
private function __clone(){
}
public static function getInstance(){
if(!(self::$_instance instanceof Single)){
self::$_instance = new self();
}
return self::$_instance ;
}
}
$a = Single::getInstance();
$b = Single::getInstance();
echo $a->rand;
echo "\n";
echo $b->rand;
两者输出的随机数是一致的, 所以可以说明"单例模式"是成功的.
使用"单例模式"连接数据库
基于上面的有效的单例模式代码, 用于连接数据库:
class Db{
private static $_instance;
private static $_connect;
private $_dbConfig = array(
'host'=>'127.0.0.1',
'user'=>'root',
'pwd'=>'vagrant',
'database'=>'tpshop',
);
private function __construct(){
}
private function __clone(){
}
public static function getInstance(){
if (!(self::$_instance instanceof self)){
self::$_instance = new self();
}
return self::$_instance;
}
public function connect(){
if(!self::$_connect){
self::$_connect = mysql_connect($this->_dbConfig['host'], $this->_dbConfig['user'], $this->_dbConfig['pwd']);
if(!self::$_connect){
die( 'MySQL connect error'.mysql_error());
}
mysql_select_db($this->_dbConfig['databas'], self::$_connect);
mysql_query('set names UTF8', self::$_connect);
}
return self::$_connect;
}
}
$connect = Db::getInstance()->connect();
var_dump($connect);
文章历史
- 2016/11/22 (第一次发布)
- 2017/03/23 开始阅读书籍"深入 PHP - 对象, 模式, 实践", 对"面向对象"有了进一步理解. 所以修改了章节"'单例模式'的特点及其逻辑推导".
- 2017/07/04 按照 PSR 代码规范,
static
需要放在"访问修饰符"前面; 修改标题;
如果你觉得我的文章对你有用, 请打个"喜欢", 或者给些改进的建议 _