1. 程式人生 > >PHP設計模式之單例模式

PHP設計模式之單例模式

blog inf DDU dbn 返回 bug 面向對象 程序員 userinfo

參考:http://www.cnblogs.com/yangjinjin/archive/2013/01/31/2887492.html

李炎恢PHP第三季視頻

單例模式

單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。

單例模式有以下3個特點:

1.只能有一個實例。

2.必須自行創建這個實例。

3.必須給其他對象提供這一實例。

PHP一個主要應用場合就是應用程序與數據庫打交道的場景,在一個應用中會存在大量的數據庫操作,針對數據庫句柄連接數據庫的行為,使用單例模式可以避免大量的new操作。因為每一次new操作都會消耗系統和內存的資源。

//初始化一個數據庫句柄$db = new DB(...);//比如有個應用場景是添加一條用戶信息

$db->addUserInfo();....../

/然而我們要在另一地方使用這個用戶信息,這時要用到數據庫句柄資源,可能會這麽做

......function test() {

   $db = new DB(...);

   $db->getUserInfo();......

有些朋友也許會說,可以直接使用global關鍵字!

   global $db;

的確global可以解決問題,也起到單例模式的作用,但在OOP中,我們拒絕這種編碼。因為global存在安全隱患(全局變量不受保護的本質)。

全局變量是面向對象程序員遇到的引發BUG的主要原因之一。這是因為全局變量將類捆綁於特定的環境,破壞了封裝。如果新的應用程序無法保證一開始就定義了相同的全局變量,那麽一個依賴於全局變量的類就無法從一個應用程序中提取出來並應用到新應用程序中。

確切的講,單例模式恰恰是對全局變量的一種改進,避免那些存儲唯一實例的全局變量汙染命名空間。你無法用錯誤類型的數據覆寫一個單例。這種保護在不支持命名空間的PHP版本裏尤其重要。因為在PHP中命名沖突會在編譯時被捕獲,並使腳本停止運行。

以實例化數據庫連接為例:

<?php
class DB{
private $_db;
//靜態可以通過類直接訪問,不需要new,private外部不能訪問
static private $_instance;
//訪問這個實例的公共靜態方法
static public function getInstance()
{
//如果對象沒有創建就創建並返回,如果已經創建過就直接返回
if

(!(self::$_instance instanceof self)){
self::$_instance = new self();
}
return self::$_instance;
}
//單一職責問題,私有化clone
private function __clone(){}
private function __construct()
{
try{
$this->_db = new PDO(‘mysql:host=127.0.0.1;dbname=shop3‘,‘root‘,‘‘);
echo ‘創建了一次數據庫連接對象‘.‘<br/>‘;
}catch (PDOException $e){
exit($e->getMessage());
}
}

public function query($sql)
{
return $this->_db->query($sql);
}
}

首先是實例化對象的時候,調用構造方法,實例化一個PDO對象,建立起數據庫連接,將句柄賦值給私有屬性$_db。但是構造方法是私有屬性,也就是說只能在類內調用.所以直接new DB()的話會報錯,無法調用DB類內的私有屬性。往上看有一個靜態方法getInstance(),靜態方法是可以通過類名直接調用並且是不需要實例化對象的。比如DB::getInstance()就可以在類外調用這個方法了。

getInstance()方法:

先判斷當前類是否被創建過,如果沒有就實例化並且賦值給靜態屬性$_instance,否則就直接返回$_instance,self::$_instance表示實例化過後DB類的句柄。也就是說不管調用多少次getInstance方法,最終也只會實例化一次DB類對象。

DB類單一職責就要求不能被clone,所以也私有化__clone方法

結論:單例模式可以很好的替代全局變量。雖然單例模式也存在和全局變量一樣的缺點

比如依賴性,因此我們應當小心謹慎的使用單例模式

PHP設計模式之單例模式