PHP單例模式(精講)

首先咱們要明確單例模式這個概念,那麼什麼是單例模式呢?php

單例模式顧名思義,就是隻有一個實例。做爲對象的建立模式,單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例,這個類咱們稱之爲單例類。html

單例模式的要點有三個:web

一是某個類只能有一個實例;數據庫

二是它必須自行建立這個實例;安全

三是它必須自行向整個系統提供這個實例。asp.net

<?php
/* 單例模式舉例,其要點以下:
 *
 * 1. $_instance 必須聲明爲靜態的私有變量
 * 2. 構造函數和克隆函數必須聲明爲私有的,這是爲了防止外部程序 new 類從而失去單例模式的意義
 * 3. getInstance()方法必須聲明爲公有的,必須調用此方法以返回惟一實例的一個引用
 * 4. ::操做符只能訪問靜態變量或靜態函數
 * 5. PHP的單例模式是相對而言的,由於PHP的解釋運行機制使得每一個PHP頁面被解釋執行後,全部的相關資源都會被回收。
 * 也就是說,PHP在語言級別上沒有辦法讓某個對象常駐內存。在PHP中,全部的變量都是頁面級的,不管是全局變量,
 * 仍是類的靜態成員,都會在頁面執行完畢後被清空,結果會從新創建新的對象,這樣也就徹底失去了Singleton的意義。
 * 不過,在實際應用中同一個頁面中可能會存在多個業務邏輯,這時單例模式就起到了很重要的做用,有效的避免了重複
 * new 對象(注: new 對象會消耗內存資源)這麼一個行爲,因此咱們說PHP的單例模式是相對而言的
 *
*/
class People {
	static private $_instance = NULL;
	public $height = '';
	public $age = '';
	private function __construct() {
		$this->height = '185';
		$this->age = 25;
	}
	private function __clone() {
		//do something
		
	}
	static public function getInstance() {
		if (!self::$_instance instanceof self) {
			//echo 'lgh-big';
			self::$_instance = new self;
		} else {
			//for testing only
			//echo 'gdc-xiaoairener';
			
		}
		return self::$_instance;
	}
	public function getHeight() {
		echo $this->height;
	}
	public function getAge() {
		echo $this->age;
	}
}
function testInstance() {
	People::getInstance()->getAge();
}
//begin to use the class
$lgh = People::getInstance();
$lgh->getHeight();
echo '<br />';
testInstance();
?>

下面咱們討論下爲何要使用PHP單例模式?函數

多數人都是從單例模式的字面上的意思來理解它的用途, 認爲這是對系統資源的節省, 能夠避免重複實例化, 是一種"計劃生育"。而PHP每次執行完頁面都是會從內存中清理掉全部的資源。於是PHP中的單例實際每次運行都是須要從新實例化的,這樣就失去了單例重複實例化的意義了。單單從這個方面來講,PHP的單例的確有點讓各位失望。可是單例僅僅只有這個功能和應用嗎?答案是否認的,咱們一塊兒來看看。測試

1. php的應用主要在於數據庫應用, 因此一個應用中會存在大量的數據庫操做,在使用面向對象的方式開發時,若是使用單例模式,則能夠避免大量的new 操做消耗的資源。this

2. 若是系統中須要有一個類來全局控制某些配置信息,那麼使用單例模式能夠很方便的實現。這個能夠參看zend Framework的FrontController部分。編碼

3. 在一次頁面請求中,便於進行調試,由於全部的代碼(例如數據庫操做類db)都集中在一個類中,咱們能夠在類中設置鉤子,輸出日誌,從而避免處處var_dump, echo。

使用傳統方式編碼

<?php
//初始化一個數據庫句柄
$db = new DB();
//好比有個應用場景是添加一條用戶信息:
$db->addUserInfo();
//然而咱們在另一個地方可能要查找用戶的信息,這個情景出如今一個函數中,這時要用到數據庫句柄資源,咱們可能須要這麼去作
function test() {
    //這時咱們不得不從新初始化一個數據庫句柄,試想多個應用場景下,這樣的代碼是多麼可怕啊?!
	$db = new DB();
	$db->getUserInfo();
	//有些朋友或許會說,我也能夠不這樣作啊,我直接利用global關鍵字不就能夠了嗎?的確,global能夠解決問題,也起到了單例模式的做用,可是OOP中,咱們拒絕這樣來編寫代碼,由於global存在安全隱患,請參考相關書籍,同時單例模式偏偏是對全局變量的一種改進,避免了那些存儲惟一實例的全局變量污染命名空間
    global $db; //OOP中,咱們不提倡這樣編寫代碼
}

使用單例模式編碼

<?php
//全部的應用情景只有一個數據庫句柄資源,嘿嘿,效率老高了,
//資源也大大的獲得節省,代碼簡潔明瞭:)
DB::getInstance()->addUserInfo();
DB::getInstance()->getUserInfo();

PHP單例模式實現的核心要點有以下三條:

1.須要一個保存類的惟一實例的靜態成員變量(一般爲$_instance私有變量)

2.構造函數和克隆函數必須聲明爲私有的,這是爲了防止外部程序new類從而失去單例模式的意義

3.必須提供一個訪問這個實例的公共的靜態方法(一般爲getInstance方法),從而返回惟一實例的一個引用

在瞭解了單例模式的應用場景以後,下面咱們經過編寫單例模式的具體實現代碼來掌握PHP單例模式的核心要點,代碼以下:

<?php
/**
 *  PHP單例模式演示舉例
 *  @author   guohua.li
 *  @modify  2010-07-11
 *  @website  http://blog.163.com/lgh_2002/
 */
class User {
	/**
	 *  靜態成品變量 保存全局實例
	 *  @access private
	 */
	static private $_instance = NULL;
	/**
	 *  私有化構造函數,防止外界實例化對象
	 */
	private function __construct() {
	}
	/**
	 *  私有化克隆函數,防止外界克隆對象
	 */
	private function __clone() {
	}
	/**
	 *  靜態方法, 單例統一訪問入口
	 *  @return  object  返回對象的惟一實例
	 */
	static public function getInstance() {
		if (is_null(self::$_instance) || !isset(self::$_instance)) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}
	/**
	 * 測試方法: 獲取用戶名字
	 */
	public function getName() {
		echo 'hello liguohua!';
	}
}

PHP單例模式的缺點

衆所周知,PHP語言是一種解釋型的腳本語言,這種運行機制使得每一個PHP頁面被解釋執行後,全部的相關資源都會被回收。也就是說,PHP在語言級別上沒有辦法讓某個對象常駐內存,這和asp.net、Java等編譯型是不一樣的,好比在Java中單例會一直存在於整個應用程序的生命週期裏,變量是跨頁面級的,真正能夠作到這個實例在應用程序生命週期中的惟一性。然而在PHP中,全部的變量不管是全局變量仍是類的靜態成員,都是頁面級的,每次頁面被執行時,都會從新創建新的對象,都會在頁面執行完畢後被清空,這樣彷佛PHP單例模式就沒有什麼意義了,因此PHP單例模式我以爲只是針對單次頁面級請求時出現多個應用場景並須要共享同一對象資源時是很是有意義的。

參考連接:

http://www.cnblogs.com/zox2011/archive/2011/09/20/2182119.html

相關文章
相關標籤/搜索