单例模式概念
单例模式是指整个应用中类只有一个对象实例的设计模式。
单例模式的特色
单例模式的特色php
单例模式的主要特色是“三私一公”:
须要一个保存类的惟一实例的私有静态成员变量
构造函数必须声明为私有的,防止外部程序new一个对象从而失去单例的意义
克隆函数必须声明为私有的,防止对象被克隆
必须提供一个访问这个实例的公共静态方法(一般命名为getInstance),从而返回惟一实例的一个引用。mysql
- 一个类在整个应用中只有一个实例
- 类必须自行建立这个实例
- 必须自行向整个系统提供这个实例
php中使用单例模式的缘由
我用php大部分操做都是和各类数据库打交道,包括mysql,redis,memcache等各类关系型和非关系型数据库,因此一个应用中会 存在大量链接数据库的操做,若是不用单例模式,那每次都要new操做,可是每次new都会消耗大量的内存资源和系统资源,并且每次打开和关闭数据库链接都 是对数据库的一种极大考验和浪费。
贴出我以前经常使用的很差的数据库链接代码,给你们一个错误示范:
- <?php
- class MysqlConn
- {
-
- const MYSQLHOSTNAME = "127.0.0.1";
- const MYSQLUSERNAME = "root";
- const MYSQLPASSWORD = "***";
- const MYSQLDBNAME = "test";
- const MYSQLCHARSET = "utf8";
-
-
- public function MysqlConnect()
- {
- $db = new mysqli(self::MYSQLHOSTNAME, self::MYSQLUSERNAEM, self::MYSQLPASSWORD, self::MYSQLDBNAME);
- $db->set_charset(self::MYSQLCHARSET);
- if (mysqli_connect_errno())
- {
- throw new CircleMysqlException("服务器系统故障", 1001);
- }
- else
- {
- return $db;
- }
- }
- }
缺陷:
每次数据库链接都要new这个类,而后调用mysqlconnect方法,返回close掉,频繁的new和数据库链接关闭操做是很是消耗资源的
改进:redis
每次应该直接返回当前应用中已经打开的数据库链接句柄sql
//有些朋友或许会说,我也能够不这样作啊,我直接利用global关键字不就能够了吗?的确,global能够解决问题,也起到了单例模式的做用,可是 OOP中,咱们拒绝这样来编写代码,由于global存在安全隐患,请参考相关书籍,同时单例模式偏偏是对全局变量的一种改进,避免了那些存储惟一实例的 全局变量污染命名空间
global $db; //OOP中,咱们不提倡这样编写代码数据库
- $db = MysqlConn::SingleMysqlConnect();
php单例模式的实现
- <?php
- class Singleton
- {
-
- private static $instance;
-
-
- private $db;
-
-
- private static function __construct()
- {
- }
-
-
- private function __clone()
- {
- }
-
-
- public static function GetInstance()
- {
- if (!(self::$instance instanceof self))
- {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
-
- public function GetDbConnect()
- {
- return $this->db;
- }
- }
- 须要一个保存类的惟一实例的静态成员变量(一般$instance为私有变量)
- 构造函数和克隆函数必须声明为私有的,为了防止外部程序new类从而失去单例模式意义
- 必须提供一个访问这个实例的公共静态方法,从而返回惟一实例的一个引用
假设咱们须要写一个类用来操做数据库,并同时知足如下要求:设计模式
①SqlHelper类只能有一个实例(不能多)
②SqlHelper类必须可以自行建立这个实例
③必须自行向整个系统提供这个实例,换句话说:多个对象共享一块内存区域,好比,对象A设置了某些属性值,则对象B,C也能够访问这些属性值(结尾的例子很好的说明了这个问题)数组

也就是说,对象A,B,C实际上都是使用同一个对象实例,访问的都是同一块内存区域
因此,即便unset($A),对象B和C仍是照样可以经过getDbName()方法输出“数据库名”的
unset($A)实际上只是将对象A与某块内存地址(该对象的实例所在的地址)之间的联系方式断开而已,跟对象B和对象C无关安全
PHP单例模式的缺点
众所周 知,PHP语言是一种解释型的脚本语言,这种运行机制使得每一个PHP页面被解释执行后,全部的相关资源都会被回收。也就是说,PHP在语言级别上没有办法 让某个对象常驻内存,这和asp.net、Java等编译型是不一样的,好比在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级的,真 正能够作到这个实例在应用程序生命周期中的惟一性。然而在PHP中,全部的变量不管是全局变量仍是类的静态成员,都是页面级的,每次页面被执行时,都会重 新创建新的对象,都会在页面执行完毕后被清空,这样彷佛PHP单例模式就没有什么意义了,因此PHP单例模式我以为只是针对单次页面级请求时出现多个应用 场景并须要共享同一对象资源时是很是有意义的。服务器
最近感受网站的数据库压力比较大,形成网站的速度降低得很厉害。由于有至关一部分的页面是直接链接数据库读数据的,因此把这部分的页面也改成使用数据库单例类来实现。如今基本都统一使用下面这个类来链接数据库了。asp.net
05 |
static private $_instance ; |
07 |
private function __construct( $host , $username , $password ) |
09 |
$this ->link = mysql_connect( $host , $username , $password ); |
10 |
$this ->query( "SET NAMES 'utf8'" , $this ->link); |
16 |
private function __clone(){} |
18 |
public static function get_class_nmdb( $host , $username , $password ) |
23 |
if ( FALSE == (self:: $_instance instanceof self) ) |
25 |
self:: $_instance = new self( $host , $username , $password ); |
27 |
return self:: $_instance ; |
31 |
public function select_db( $database ) |
33 |
$this ->result = mysql_select_db( $database ); |
38 |
public function query( $query ) |
40 |
return $this ->result = mysql_query( $query , $this ->link); |
44 |
public function fetch_array( $fetch_array ) |
46 |
return $this ->result = mysql_fetch_array( $fetch_array , MYSQL_ASSOC); |
50 |
public function num_rows( $query ) |
52 |
return $this ->result = mysql_num_rows( $query ); |
56 |
public function close() |
58 |
return $this ->result = mysql_close( $this ->link); |
这个类的使用以下:
1 |
$connector = nmdb::get_class_nmdb( $host , $username , $password ); |
2 |
$connector -> select_db( $database ); |
下面的类也能够参考下:
06 |
private $host = 'localhost' ; |
07 |
private $user = 'root' ; |
09 |
private $database = 'imoro_imoro' ; |
10 |
private $charset = 'utf8' ; |
18 |
private function __construct( $pconnect = false) { |
20 |
$this ->link = @ mysql_connect( $this ->host, $this ->user, $this ->pwd) or $this ->err(); |
22 |
$this ->link = @ mysql_pconnect( $this ->host, $this ->user, $this ->pwd) or $this ->err(); |
24 |
mysql_select_db( $this ->database) or $this ->err(); |
25 |
$this ->query( "SET NAMES '{$this->charset}'" , $this ->link); |
32 |
private function __clone(){} |
33 |
public static function getInstance( $pconnect = false){ |
34 |
if (FALSE == (self:: $_instance instanceof self)){ |
35 |
self:: $_instance = new self( $pconnect ); |
37 |
return self:: $_instance ; |
42 |
public function query( $sql , $link = '' ) { |
43 |
$this ->result = mysql_query( $sql , $this ->link) or $this ->err( $sql ); |
49 |
public function getRow( $sql , $type = MYSQL_ASSOC) { |
50 |
$result = $this ->query( $sql ); |
51 |
return @ mysql_fetch_array( $result , $type ); |
56 |
public function getRows( $sql , $type = MYSQL_ASSOC) { |
57 |
$result = $this ->query( $sql ); |
58 |
while ( $row = @ mysql_fetch_array( $result , $type )) { |
66 |
protected function err( $sql = null) { |
73 |
$db = mysql::getInstance(); |
74 |
$db2 = mysql::getInstance(); |
75 |
$data = $db ->getRows( 'select * from blog' ); |