亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1679 | 回復(fù): 0
打印 上一主題 下一主題

《PHP設(shè)計(jì)模式介紹》第四章 單件模式 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-12-10 20:38 |只看該作者 |倒序?yàn)g覽

幾乎所有面向?qū)ο蟮某绦蛑,總有一兩個資源被創(chuàng)建出來,在程序應(yīng)用中持續(xù)被共享使用。例如,這樣的一個資源,在一個電子商務(wù)程序的數(shù)據(jù)庫連接中使
用:這個連接在應(yīng)用程序啟動時(shí)初始化,程序于是可以有效的執(zhí)行;當(dāng)程序結(jié)束時(shí),這個連接最終被斷開并銷毀。如果是你寫的代碼,沒必要在每時(shí)每刻創(chuàng)建一個數(shù)
據(jù)庫連接,這樣非常低效。已經(jīng)建立好的連接應(yīng)該能被你的代碼簡單重復(fù)的使用。這個問題就是,基于以上要求你將如何進(jìn)行這個數(shù)據(jù)庫連接?(或者連接其它被循
環(huán)使用的唯一資源,比如一個開放文件或者一個隊(duì)列。)
問題
你怎樣確保一個特殊類的實(shí)例是獨(dú)一無二的(它是這個類的唯一實(shí)例),并且它很存取容易呢?
解決方案
當(dāng)然,全局變量是顯而易見的解決方案。但它就像潘多拉的盒子
(正確的判斷來自經(jīng)驗(yàn),而錯誤的判斷產(chǎn)生經(jīng)驗(yàn)。這句諺語就是這個意思。),你的任何代碼都能修改全局變量,這將不可避免的引起更多調(diào)試的意外。換句話說,
全局變量的狀態(tài)總是會出現(xiàn)一些問題的,(這里有一個關(guān)于全局變量使用問題不錯的描述,http://c2.com/cgi
/wiki?GlobalVariablesAreBad)。
當(dāng)你需要一個特殊類的唯一實(shí)例時(shí),使用這個名字叫單件的模式;趩渭J降念惸軐(shí)例化和初始化這個類的一個實(shí)例,并且提供每時(shí)每刻絕對相同的連接。一般情況下使用名為getInstance()的靜態(tài)方法實(shí)現(xiàn)。
關(guān)鍵問題是,如何在每時(shí)每刻獲得一個精確統(tǒng)一的實(shí)例。請看下面的例子:
   
        
            
            class DbConn {
function DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error(‘The DbConn class is a Singleton,’
.’ please do not instantiate directly.’);
}
}
function &getInstance() {
$key = ‘__some_unique_key_for_the_DbConn_instance__’;
if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key])
&& ‘dbconn’ == get_class($GLOBALS[$key]) )) {
$GLOBALS[$key] =& new DbConn(M_E);
}
return $GLOBALS[$key];
}
}
            
        
   
注釋:assertReference
assertReference() 方法確保兩個被傳遞的參數(shù)引用自相同的PHP變量。   
在PHP4中,這里斷言兩個被測試的參數(shù)的卻是相同的對象。assertReference() 這個方法在移植到PHP5以后也許就不推薦使用了。
這個test方法有兩個斷言:第一個判斷第調(diào)用靜態(tài)方法DbConn::getInstance()返回的值是DbConn對象的實(shí)例,第二個用來判斷第二次調(diào)用getInstance()方法返回得值引用的是相同的對象實(shí)例,這意味著他們使用的是同一個對象。
除了斷言代碼預(yù)期的執(zhí)行結(jié)果,Test也預(yù)示了getInstance()的正確用法(PHP4):$local_conn_var=&DbConn::getInstance()。引用(=&)靜態(tài)方法的返回值賦值給了這個局部變量。
再寫另外一段測試代碼:直接用“new”來實(shí)例化一個單件類會引起某些類型的錯誤。test代碼如下:
   
        
            [color="#ff0000"]function TestBadInstantiate() {
$obj =& new DbConn;
$this->assertErrorPattern(
‘/(bad|nasty|evil|do not|don\’t|warn).*’.
‘(instance|create|new|direct)/i’);
}
        
   

樣本代碼
單件模式是一個很有趣的模式。讓我們用PHP4和PHP5兩種方式來探究它的實(shí)現(xiàn)過程,現(xiàn)在從PHP4開始。
全局方式

論上說,一個全局變量可以生成一個完美的單件,但全局變量可能被修改:在代碼運(yùn)行過程中,不能保證全局變量指向的是一個對象。因而,不讓全局變量在全局直
接引用,就可以減少“太隨意訪問”這個全局變量的問題。比如說,這段代碼使用一個非常長而且獨(dú)特的名字,從而“隱藏”了全局變量的引用。
   
        
            [color="#ff0000"] class DbConn {
function DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error(‘The DbConn class is a Singleton,’
.’ please do not instantiate directly.’);
}
}
function &getInstance() {
$key = ‘__some_unique_key_for_the_DbConn_instance__’;
if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key])
&& ‘dbconn’ == get_class($GLOBALS[$key]) )) {
$GLOBALS[$key] =& new DbConn(M_E);
}
return $GLOBALS[$key];
}
}
        
   

表示成一個UML類圖,解決辦法如下:


如果你不選用這個“神秘參數(shù)”-類型保護(hù),建立一個全局標(biāo)記是另外一個選擇,用它來驗(yàn)證你是通過getInstance()方法來創(chuàng)建的對象。保護(hù)方式從“你知道它的名字”改變成“它存在于環(huán)境中”。
下面有個例子,它解釋了為什么構(gòu)造函數(shù)保護(hù)代碼有一個全局的標(biāo)識:
   
        
            
            class DbConn {
function DbConn() {
$token = ‘__some_DbConn_instance_create_semaphore__’;
if (!array_key_exists($token, $GLOBALS)) {
trigger_error(‘The DbConn class is a Singleton,’
.’ please do not instantiate directly.’);
}
}
function &getInstance() {
static $instance = array();
if (!$instance) {
$token = ‘__some_DbConn_instance_create_semaphore__’;
$GLOBALS[$token] = true;
$instance[0] =& new DbConn;
unset($GLOBALS[$token]);
}
return $instance[0];
}
}
            
        
   

PHP4允許你改變構(gòu)造函數(shù)中$this的值。在過去,我們會習(xí)慣設(shè)置 $this = null;當(dāng)有一個創(chuàng)建構(gòu)造錯誤時(shí),確保無效的對象不能被代碼繼續(xù)使用。PHP4中很有用的東西,在PHP5中并不兼容,將來會在你的代碼中得到驗(yàn)證,這種技術(shù)不再被推薦。
這段代碼中另外一個重點(diǎn)是引用操作&的用法。有兩種地方需要使用&。第一種
是在函數(shù)定義時(shí),在函數(shù)名字前用來表示將返回一個引用。第二種是將新的DbConn對象賦值給$GLOBALS數(shù)組。(在序言和值對象章節(jié)中提到過:在
PHP4中,你總會使用&操作符,以引用的方式創(chuàng)建、傳遞和返回對象,)
getInstance()方法的條件檢查,常常被寫成沒有警示的情況下運(yùn)行,甚至在
E_ALL的錯誤級別下也不會提示。它檢查在$GLOBAL數(shù)組中適當(dāng)?shù)奈恢檬欠裼幸粋DbConn對象,如果沒有,就在那里創(chuàng)建這個對象。這個方法于是
返回了這樣的結(jié)果,這個對象能被重復(fù)創(chuàng)建或者這個對象在之前已經(jīng)被這個方法創(chuàng)建過了。當(dāng)方法結(jié)束時(shí),你可以確認(rèn)已經(jīng)擁有這個類的有效實(shí)例,而且它已經(jīng)被有
效初始化。
靜態(tài)方式
關(guān)于全局變量的問題,甚至隱藏在getInstance()中的全局變量中也存在。因?yàn)槿肿兞吭谀_本的任何地方都有效,在沒有注意到的情況下,你依然有可能破壞這個全局變量,
在getInstance()方法內(nèi)部使用靜態(tài)變量來存儲Singleton是一個顯得干凈的辦法。第一個代碼片斷如下:
   
        
            [color="#ff0000"]class DbConn {
// ...
function &getInstance() {
static $instance = false;
if (!$instance) $instance =& new DbConn(M_E);
return $instance;
}
}
        
   

   
        
            [color="#ff0000"]class DbConn {
function DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error(‘The DbConn class is a Singleton,’
.’ please do not instantiate directly.’);
}
}
function &getInstance() {
static $instance = array();
if (!$instance) $instance0 =& new DbConn(M_E);
return $instance0;
}
}
        
   

雖然這段代碼有點(diǎn)依賴PHP的布爾方式,但它比那個全局版本更嚴(yán)謹(jǐn):在條件檢測時(shí),使用一個空的數(shù)組會得到結(jié)果false。就像在DbConn類的前一個版本一樣,在函數(shù)的定義和賦值部分需要引用操作符。
PHP5中的單件模式
PHP5中更容易實(shí)現(xiàn)單件模式,PHP5對于類內(nèi)部變量和函數(shù)的訪問控制被加強(qiáng)了。將DbConn::_construct()構(gòu)造方法設(shè)置為私有(private),這個類就不能被直接實(shí)例化。用UML圖表示,PHP5的DbConn單件模式如下:


組合使用靜態(tài)方法和靜態(tài)變量保持這個實(shí)例,并且設(shè)置構(gòu)造函數(shù)為私有,以防止直接實(shí)例化類而創(chuàng)建實(shí)例,代碼如下:
   
        
            [color="#ff0000"]class DbConn {
/**
* static property to hold singleton instance
*/
static $instance = false;
/**
* constructor
* private so only getInstance() method can instantiate
* @return void
*/
private function __construct() {}
/**
* factory method to return the singleton instance
* @return DbConn
*/
public function getInstance() {
if (!DbConn::$instance) {
DbConn::$instance = new DbConn;
}
return DbConn::$instance;
}
}
        
   
結(jié)論
現(xiàn)在你已經(jīng)看到幾種單件設(shè)計(jì)模式的實(shí)現(xiàn)方式了,當(dāng)你著眼于實(shí)現(xiàn)這個設(shè)計(jì)模式時(shí)候,我們要仔細(xì)權(quán)衡考慮。
首先,一個單件對象不是一個“很好的”全局變量。舉例來說,如果一個方法需要一個單件對象,更顯而易見的用法是把它當(dāng)作一個參數(shù)傳遞來使用。
同樣,因?yàn)樗梢栽凇叭帧钡玫绞褂茫愫苋菀拙蜁䦟⑺蟹N類的“常用函數(shù)”放置到一個單件類中。這個是你需要避免的,需要的時(shí)候,你才把函數(shù)封裝單件類中。
Monostate Pattern(單態(tài)模式):類單件模式
有時(shí)
候,我們需要這樣一個類,所有類的實(shí)例都共享它的全局狀態(tài)――換句話說,它所產(chǎn)生的任何實(shí)例返回嚴(yán)格一致的信息。和單件模式類似,這是一種叫做
MonoState(單態(tài))的模式。在PHP中,你使用一種優(yōu)美的引用技巧來綁定全局?jǐn)?shù)據(jù),通過把一個全局變量綁定到一個實(shí)例變量來實(shí)現(xiàn)
MonoState。
舉個例子,讓我們創(chuàng)建一個全局應(yīng)用配置類。無論你用MonoState類的哪個實(shí)例,你都能得到同樣的值。
下面的測試代碼用來驗(yàn)證次功能:
   
        
            [color="#ff0000"]// PHP4
function TestApplConfig() {
$this->assertIsA(
$obj1 =& new ApplicationConfig, ‘ApplicationConfig’);
$this->assertIsA(
$obj2 =& new ApplicationConfig, ‘ApplicationConfig’);
$test_val = ‘/path/to/cache’.rand(1,100);
$obj1->set(‘cache_path’, $test_val);
$this->assertEqual($test_val, $obj2->get(‘cache_path’));
}
        
   

下面這段代碼實(shí)現(xiàn)了MonoState:
   
        
            [color="#ff0000"]class ApplicationConfig {
var $_state;
function ApplicationConfig() {
$key = ‘__stealth_singleton_state_index__’;
if (!(array_key_exists($key, $GLOBALS)
&& is_array($GLOBALS[$key]))) {
$GLOBALS[$key] = array();
}
$this->_state =& $GLOBALS[$key];
}
function set($key, $val) {
$this->_state[$key] = $val;
}
function get($key) {
if (array_key_exists($key, $this->_state)) {
return $this->_state[$key];
}
}
        
   

這個技巧能夠在任何PHP的自動全局(superglobal)數(shù)組使用,尤其在用戶消息隊(duì)
列$_SESSION中有很顯著的效果。MonoState能通過你的代碼為用戶存儲一系列的使用信息(你要顯示的信息可能是從另外一個頁面?zhèn)魅?br /> 的)。$_SESSION是一個存儲這些信息的好地方,以方便這些信息在頁面跳轉(zhuǎn)后能持續(xù)使用。
這個技巧的核心是$this->state =& $GLOBALS[$key];
。在確定$GLOBALS[$key]是一個數(shù)組后,代碼綁定一個全局?jǐn)?shù)組的引用給類變量$this->state。從而,任
何$this->state的改變都自然而言地同步到全局?jǐn)?shù)組,包括類的其它實(shí)例。Test創(chuàng)建了MonoStatel類的兩個不同
實(shí)例,改變其中一個,驗(yàn)證另外一個是否也一起被改變了。這段代碼很簡單的選擇了這個靜態(tài)數(shù)組$instancede的第一個元素,用來保持單件
DbConns實(shí)例的引用。Zend 1引擎在PHP4中不能存儲靜態(tài)變量的引用
(請看http://www.php.net/manual/en/language.variables.scope.php#AEN3609)。使用
一個工作區(qū)存儲靜態(tài)數(shù)組,并且將這個單件實(shí)例的引用放置到一個已知的數(shù)組中。getInstance()方法如下:提示在
DbConn的構(gòu)造函數(shù)中,你可能對$fromGetInstance的默認(rèn)參數(shù)感到疑惑。在對象被直接實(shí)例化時(shí),它能夠提供(很微弱的)保護(hù):除非這個
默認(rèn)值變成e (在PHP的數(shù)學(xué)常量中 M_E = 2.718281828459),否則這段代碼會報(bào)錯。這段代碼直接創(chuàng)建了一個 DbConn
的實(shí)例,將會引起PHP報(bào)錯。為了讓代碼更穩(wěn)定,我們用PCRE正則表達(dá)式來匹配報(bào)錯信息。(顯示報(bào)錯信息的確切措詞并不重要。)
               
               
               

本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u2/84304/showart_1713203.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP