單例模式概念

單例模式概念

單例模式是指整個應用中類只有一個對象實例的設計模式。

單例模式的特色

單例模式的特色php

單例模式的主要特色是「三私一公」:
須要一個保存類的惟一實例的私有靜態成員變量
構造函數
必須聲明爲私有的,防止外部程序new一個對象從而失去單例的意義
克隆函數必須聲明爲私有的,防止對象被克隆
必須提供一個訪問這個實例的公共靜態方法(一般命名爲getInstance),從而返回惟一實例的一個引用。mysql

  • 一個類在整個應用中只有一個實例
  • 類必須自行建立這個實例
  • 必須自行向整個系統提供這個實例

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和數據庫鏈接關閉操做是很是消耗資源的

改進:redis

每次應該直接返回當前應用中已經打開的數據庫鏈接句柄sql

//有些朋友或許會說,我也能夠不這樣作啊,我直接利用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單例模式我以爲只是針對單次頁面級請求時出現多個應用 場景並須要共享同一對象資源時是很是有意義的。服務器

 

 

最近感受網站的數據庫壓力比較大,形成網站的速度降低得很厲害。由於有至關一部分的頁面是直接鏈接數據庫讀數據的,因此把這部分的頁面也改成使用數據庫單例類來實現。如今基本都統一使用下面這個類來鏈接數據庫了。asp.net

01 <?php
02 class 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 */
05 class 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 //判斷兩個對象是否相等
78 if($db === $db2){
79     echo 'true';
80 }
81 ?>
相關文章
相關標籤/搜索