单例模式概念

单例模式概念

单例模式是指整个应用中类只有一个对象实例的设计模式。

单例模式的特点

单例模式的特点

单例模式的主要特点是“三私一公”:
需要一个保存类的唯一实例的私有静态成员变量
构造函数
必须声明为私有的,防止外部程序new一个对象从而失去单例的意义
克隆函数必须声明为私有的,防止对象被克隆
必须提供一个访问这个实例的公共静态方法(通常命名为getInstance),从而返回唯一实例的一个引用。

  • 一个类在整个应用中只有一个实例
  • 类必须自行创建这个实例
  • 必须自行向整个系统提供这个实例

php中使用单例模式的原因

我用php大部分操作都是和各种数据库打交道,包括mysql,redis,memcache等各种关系型和非关系型数据库,所以一个应用中会 存在大量连接数据库的操作,如果不用单例模式,那每次都要new操作,但是每次new都会消耗大量的内存资源和系统资源,而且每次打开和关闭数据库连接都 是对数据库的一种极大考验和浪费。
贴出我之前常用的不好的数据库连接代码,给大家一个错误示范:
  1. <?php  
  2. class MysqlConn  
  3. {  
  4.     // MYSQL数据库连接信息  
  5.     const MYSQLHOSTNAME = "127.0.0.1";  
  6.     const MYSQLUSERNAME = "root";  
  7.     const MYSQLPASSWORD = "***";  
  8.     const MYSQLDBNAME = "test";  
  9.     const MYSQLCHARSET = "utf8";  
  10.       
  11.     /** 
  12.     * Description:mysql数据库连接函数 
  13.     * Return value:连接成功返回数据库连接句柄;连接失败返回错误消息 
  14.     */  
  15.     public function MysqlConnect()  
  16.     {  
  17.         $db = new mysqli(self::MYSQLHOSTNAME, self::MYSQLUSERNAEM, self::MYSQLPASSWORD, self::MYSQLDBNAME); // 连接数据库  
  18.         $db->set_charset(self::MYSQLCHARSET);  
  19.         if (mysqli_connect_errno())  
  20.         {  
  21.             throw new CircleMysqlException("服务器系统故障", 1001);  
  22.         }  
  23.         else  
  24.         {  
  25.             return $db;  
  26.         }  
  27.     }  
  28. }  

缺陷:
每次数据库连接都要new这个类,然后调用mysqlconnect方法,返回close掉,频繁的new和数据库连接关闭操作是非常消耗资源的

改进:

每次应该直接返回当前应用中已经打开的数据库连接句柄

//有些朋友或许会说,我也可以不这样做啊,我直接利用global关键字不就可以了吗?的确,global可以解决问题,也起到了单例模式的作用,但是 OOP中,我们拒绝这样来编写代码,因为global存在安全隐患,请参考相关书籍,同时单例模式恰恰是对全局变量的一种改进,避免了那些存储唯一实例的 全局变量污染命名空间
 global $db;  //OOP中,我们不提倡这样编写代码

  1. //单例模式返回数据库连接句柄  
  2. $db = MysqlConn::SingleMysqlConnect();  

php单例模式的实现

  1. <?php  
  2. class Singleton  
  3. {  
  4.     /** 
  5.      * Description:(1)静态变量,保存全局实例,跟类绑定,跟对象无关 
  6.      *             (2)私有属性,为了避免类外直接调用 类名::$instance,防止为空 
  7.      */  
  8.     private static $instance;  
  9.       
  10.     /** 
  11.      * Description:数据库连接句柄 
  12.      */  
  13.     private $db;  
  14.       
  15.     /** 
  16.      * Description:私有化构造函数,防止外界实例化对象 
  17.      */  
  18.     private static function __construct()  
  19.     {  
  20.     }  
  21.       
  22.     /** 
  23.      * Description:私有化克隆函数,防止外界克隆对象 
  24.      */  
  25.     private function __clone()  
  26.     {  
  27.     }  
  28.       
  29.     /** 
  30.      * Description:静态方法,单例访问统一入口 
  31.      * @return Singleton:返回应用中的唯一对象实例 
  32.      */  
  33.     public static function GetInstance()  
  34.     {  
  35.         if (!(self::$instance instanceof self))  
  36.         {  
  37.             self::$instance = new self();  
  38.         }  
  39.         return self::$instance;  
  40.     }  
  41.       
  42.     /** 
  43.      * Description:获取数据库的私有方法的连接句柄 
  44.      */  
  45.     public function GetDbConnect()  
  46.     {  
  47.         return $this->db;  
  48.     }  
  49. }  
    • 需要一个保存类的唯一实例的静态成员变量(通常$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单例模式我觉得只是针对单次页面级请求时出现多个应用 场景并需要共享同一对象资源时是非常有意义的。

 

 

最近感觉网站的数据库压力比较大,造成网站的速度下降得很厉害。因为有相当一部分的页面是直接连接数据库读数据的,所以把这部分的页面也改为使用数据库单例类来实现。现在基本都统一使用下面这个类来连接数据库了。

01<?php
02class nmdb
03{
04    private $link;
05    static private $_instance;
06    // 连接数据库
07    private function __construct($host, $username, $password)
08    {
09        $this->link = mysql_connect($host, $username, $password);
10        $this->query("SET NAMES 'utf8'", $this->link);
11        //echo mysql_errno($this->link) . ": " . mysql_error($link). "n";
12        //var_dump($this->link);
13        return $this->link;
14    }
15     
16    private function __clone(){}
17     
18    public static function get_class_nmdb($host, $username, $password)
19    {
20        //$connector = new nmdb($host, $username, $password);
21        //return $connector;
22         
23        if( FALSE == (self::$_instance instanceof self) )
24        {
25            self::$_instance = new self($host, $username, $password);
26        }
27        return self::$_instance;
28    }
29     
30    // 连接数据表
31    public function select_db($database)
32    {
33        $this->result = mysql_select_db($database);
34        return $this->result;
35    }
36     
37    // 执行SQL语句
38    public function query($query)
39    {
40        return $this->result = mysql_query($query, $this->link);
41    }
42     
43    // 将结果集保存为数组
44    public function fetch_array($fetch_array)
45    {
46        return $this->result = mysql_fetch_array($fetch_array, MYSQL_ASSOC);
47    }
48     
49    // 获得记录数目
50    public function num_rows($query)
51    {
52        return $this->result = mysql_num_rows($query);
53    }
54     
55    // 关闭数据库连接
56    public function close()
57    {
58        return $this->result = mysql_close($this->link);
59    }
60     
61}
62?>

这个类的使用如下:

1$connector = nmdb::get_class_nmdb($host, $username, $password);
2$connector -> select_db($database);

下面的类也可以参考下:

01<?php
02/*
03* mysql 单例
04*/
05class mysql{
06    private $host    ='localhost'; //数据库主机
07    private $user     = 'root'; //数据库用户名
08    private $pwd     = ''; //数据库用户名密码
09    private $database = 'imoro_imoro'; //数据库名
10    private $charset = 'utf8'; //数据库编码,GBK,UTF8,gb2312
11    private $link;             //数据库连接标识;
12    private $rows;             //查询获取的多行数组
13    static $_instance; //存储对象
14    /**
15     * 构造函数
16     * 私有
17     */
18    private function __construct($pconnect = false) {
19        if (!$pconnect) {
20            $this->link = @ mysql_connect($this->host, $this->user, $this->pwd) or $this->err();
21        } else {
22            $this->link = @ mysql_pconnect($this->host, $this->user, $this->pwd) or $this->err();
23        }
24        mysql_select_db($this->database) or $this->err();
25        $this->query("SET NAMES '{$this->charset}'", $this->link);
26        return $this->link;
27    }
28    /**
29     * 防止被克隆
30     *
31     */
32    private function __clone(){}
33    public static function getInstance($pconnect = false){
34        if(FALSE == (self::$_instance instanceof self)){
35            self::$_instance = new self($pconnect);
36        }
37        return self::$_instance;
38    }
39    /**
40     * 查询
41     */
42    public function query($sql, $link = '') {
43        $this->result = mysql_query($sql, $this->link) or $this->err($sql);
44        return $this->result;
45    }
46    /**
47     * 单行记录
48     */
49    public function getRow($sql, $type = MYSQL_ASSOC) {
50        $result = $this->query($sql);
51        return @ mysql_fetch_array($result, $type);
52    }
53    /**
54     * 多行记录
55     */
56    public function getRows($sql, $type = MYSQL_ASSOC) {
57        $result = $this->query($sql);
58        while ($row = @ mysql_fetch_array($result, $type)) {
59            $this->rows[] = $row;
60        }
61        return $this->rows;
62    }
63    /**
64     * 错误信息输出
65     */
66    protected function err($sql = null) {
67        //这里输出错误信息
68        echo 'error';
69        exit();
70    }
71}
72//用例
73$db = mysql::getInstance();
74$db2 = mysql::getInstance();
75$data = $db->getRows('select * from blog');
76//print_r($data);
77//判断两个对象是否相等
78if($db === $db2){
79    echo 'true';
80}
81?>

转载于:https://www.cnblogs.com/kekjiuyue/p/5901482.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值