單例模式概念
單例模式是指整個應用中類只有一個對象實例的設計模式。
單例模式的特色
單例模式的特色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' ); |