摘要:單例模式顧名思義,就是只有一個(gè)實(shí)例。為什么要使用單例模式語言本身的局限性語言是一種解釋型的腳本語言,這種運(yùn)行機(jī)制使得每個(gè)頁面被解釋執(zhí)行后,所有的相關(guān)資源都會(huì)被回收。
單例模式(Singleton Pattern):顧名思義,就是只有一個(gè)實(shí)例。作為對象的創(chuàng)建模式,單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。為什么要使用單例模式
1、PHP語言本身的局限性
PHP語言是一種解釋型的腳本語言,這種運(yùn)行機(jī)制使得每個(gè)PHP頁面被解釋執(zhí)行后,所有的相關(guān)資源都會(huì)被回收。也就是說,PHP在語言級別上沒有辦法讓某個(gè)對象常駐內(nèi)存,這和asp.NET、Java等編譯型是不同的,比如在Java中單例會(huì)一直存在于整個(gè)應(yīng)用程序的生命周期里,變量是跨頁面級的,真正可以做到這個(gè)實(shí)例在應(yīng)用程序生命周期中的唯一性。然而在PHP中,所有的變量無論是全局變量還是類的靜態(tài)成員,都是頁面級的,每次頁面被執(zhí)行時(shí),都會(huì)重新建立新的對象,都會(huì)在頁面執(zhí)行完畢后被清空,這樣似乎PHP單例模式就沒有什么意義了,所以PHP單例模式我覺得只是針對單次頁面級請求時(shí)出現(xiàn)多個(gè)應(yīng)用場景并需要共享同一對象資源時(shí)是非常有意義的。
2、應(yīng)用場景
一個(gè)應(yīng)用中會(huì)存在大量的數(shù)據(jù)庫操作,比如過數(shù)據(jù)庫句柄來連接數(shù)據(jù)庫這一行為,使用單例模式可以避免大量的new操作,因?yàn)槊恳淮蝞ew操作都會(huì)消耗內(nèi)存資源和系統(tǒng)資源。
如果系統(tǒng)中需要有一個(gè)類來全局控制某些配置信息,那么使用單例模式可以很方便的實(shí)現(xiàn).
一個(gè)類只能有一個(gè)對象
必須是自行創(chuàng)建這個(gè)類的對象
要想整個(gè)系統(tǒng)提供這一個(gè)對象
具體實(shí)現(xiàn)的重點(diǎn)單例模式的類只提供私有的構(gòu)造函數(shù),
類定義中含有一個(gè)該類的靜態(tài)私有對象,
該類提供了一個(gè)靜態(tài)的公有的函數(shù)用于創(chuàng)建或獲取它本身的靜態(tài)私有對象。
代碼實(shí)現(xiàn)class Singleton{ //存放實(shí)例 私有靜態(tài)變量 private static $_instance = null; //私有化構(gòu)造方法、 private function __construct(){ echo "單例模式的實(shí)例被構(gòu)造了"; } //私有化克隆方法 private function __clone(){ } //公有化獲取實(shí)例方法 public static function getInstance(){ if (!(self::$_instance instanceof Singleton)){ self::$_instance = new Singleton(); } return self::$_instance; } } $singleton=Singleton::getInstance();OOP知識補(bǔ)習(xí) 類型運(yùn)算符instanceof
以上例程會(huì)輸出:
bool(true) bool(false)
instanceof用于確定一個(gè)變量是不是實(shí)現(xiàn)了某個(gè)類,繼承類,接口的對象的實(shí)例。
如果被檢測的變量不是對象,instanceof 并不發(fā)出任何錯(cuò)誤信息而是返回 FALSE。不允許用來檢測常量。
構(gòu)造方法聲明為private,防止直接創(chuàng)建對象 ,這樣new Singleton() 會(huì)報(bào)錯(cuò)。
private function __construct()
{
echo "Iam constructed";
}
魔術(shù)方法__clone()當(dāng)類的復(fù)制完成時(shí),如果定義了__clone()方法,則新創(chuàng)建的對象(復(fù)制生成的對象)中的__clone() 方法會(huì)被調(diào)用,可用于修改屬性的值(如果有必要的話)。私有化__clone可以防止克隆該類的對象。
注意一點(diǎn):clone的對象不執(zhí)行__construct里的方法
所以我們在防止單例模式的 $singleton對象被clone,有兩種方法可以做到。
第一種方法:設(shè)置魔術(shù)方法__clone();訪問權(quán)限為private
第二種方法:若__clone()為公用方法,則在函數(shù)中加上自定義錯(cuò)誤。
// 阻止用戶復(fù)制對象實(shí)例 public function __clone(){ trigger_error("Clone is not allowed.",E_USER_ERROR); }
關(guān)于 __clone() , PHP官方的文檔: Once the cloing is complete, if a __clone() method is defined, then the newly created object’s __clone() method will be called, to allow any necessary properties that need to be changed.關(guān)鍵字clone和賦值
class foo { public $bar = "php"; } $foo = new foo(); $a = $foo; // 標(biāo)識符賦值(把$a賦值為null,原來的$foo并不會(huì)變成null,但通過$a能夠修改$foo的成員$bar) $a = &$foo; // 引用賦值(把$a賦值為null,原來的$foo也會(huì)跟著變成null) $a = clone $foo; // 值賦值(賦值后互不影響,在計(jì)算機(jī)內(nèi)存上的體現(xiàn)屬于淺復(fù)制)對象復(fù)制
在PHP中, 對象間的賦值操作實(shí)際上是引用操作 (事實(shí)上,絕大部分的編程語言都是如此! 主要原因是內(nèi)存及性能的問題) , 比如 :
class myclass { public $data; } $obj1 = new myclass(); $obj1->data = "aaa"; $obj2 = $obj1; $obj2->data ="bbb"; //$obj1->data的值也會(huì)變成"bbb"
因?yàn)?obj1和$obj2都是指向同一個(gè)內(nèi)存區(qū)的引用,所以修改任何一個(gè)對象都會(huì)同時(shí)修改另外一個(gè)對象。
在有些時(shí)候,我們其實(shí)不希望這種reference式的賦值方式, 我們希望能完全復(fù)制一個(gè)對象,這是侯就需要用到 Php中的clone (對象復(fù)制)。
class myclass { public $data; } $obj1 = new myclass(); $obj1->data ="aaa"; $obj2 = clone $obj1; $obj2->data ="bbb"; // $obj1->data的值仍然為"aaa"
因?yàn)閏lone的方式實(shí)際上是對整個(gè)對象的內(nèi)存區(qū)域進(jìn)行了一次復(fù)制并用新的對象變量指向新的內(nèi)存, 因此賦值后的對象和源對象相互之間是基本來說獨(dú)立的。
淺復(fù)制什么? 基本獨(dú)立?!這是什么意思? 因?yàn)镻HP的object clone采用的是淺復(fù)制(shallow copy)的方法, 如果對象里的屬性成員本身就是reference類型的,clone以后這些成員并沒有被真正復(fù)制,仍然是引用的。 (事實(shí)上,其他大部分語言也是這樣實(shí)現(xiàn)的, 如果你對C++的內(nèi)存,拷貝,copy constructor等概念比較熟悉,就很容易理解這個(gè)概念), 下面是一個(gè)例子來說明:
class myClass{ public $data; } $sss ="aaa"; $obj1 = new myClass(); $obj1->data =&$sss; //注意,這里是個(gè)reference! $obj2 = clone $obj1; $obj2->data="bbb"; //這時(shí),$obj1->data的值變成了"bbb" 而不是"aaa"! var_dump($obj1); var_dump($obj2);
我們再舉一個(gè)更實(shí)用的例子來說明一下PHP clone這種淺復(fù)制帶來的后果:
class testClass { public $str_data; public $obj_data; } $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC")); $obj1 = new testClass(); $obj1->str_data ="aaa"; $obj1->obj_data = $dateTimeObj; $obj2 = clone $obj1; var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" $obj2->str_data ="bbb"; $obj2->obj_data->add(new DateInterval("P10D")); //給$obj2->obj_date 的時(shí)間增加了10天 var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" !!!! var_dump($obj2); // str_data:"bbb" obj_data:"2014-07-15 00:00:00" var_dump($dateTimeObj) // 2014-07-15 00:00:00"
這一下可以更加清楚的看到問題了吧。 一般來講,你用clone來復(fù)制對象,希望是把兩個(gè)對象徹底分開,不希望他們之間有任何關(guān)聯(lián), 但由于clone的shallow copy的特性, 有時(shí)候會(huì)出現(xiàn)非你期望的結(jié)果.
深復(fù)制1) $obj1->obj_data =$dateTimeObj 這句話實(shí)際上是個(gè)引用類型的賦值. 還記得前面提到的PHP中對象直接的賦值是引用操作么?除非你用$obj1->obj_dat = clone $dataTimeObj!
2) $obj2 = clone $obj1 這句話生成了一個(gè)obj1對象的淺復(fù)制對象,并賦給obj2. 由于是淺復(fù)制,obj2中的obj_data也是對$dateTimeObj的引用!
3)$dateTimeObj, $obj1->obj_data, $obj2->obj_data 實(shí)際上是同一個(gè)內(nèi)存區(qū)對象數(shù)據(jù)的引用,因此修改其中任何一個(gè)都會(huì)影響其他兩個(gè)!
如何解決這個(gè)問題呢? 采用PHP中的 __clone方法 把淺復(fù)制轉(zhuǎn)換為深復(fù)制(這個(gè)方法給C++中的copy constructor概念上有些相似,但執(zhí)行流程并不一樣)
class testClass { public $str_data; public $obj_data; public function __clone() { $this->obj_data = clone $this->obj_data; } $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC")); $obj1 = new testClass(); $obj1->str_data ="aaa"; $obj1->obj_data = $dateTimeObj; $obj2 = clone $obj1; var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" $obj2->str_data ="bbb"; $obj2->obj_data->add(new DateInterval("P10D")); var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" var_dump($dateTimeObj); //"2014-07-05 00:00:00"
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/29321.html
摘要:二為什么用單例實(shí)際項(xiàng)目中像數(shù)據(jù)庫查詢,日志輸出,全局回調(diào),統(tǒng)一校驗(yàn)等模塊。單例模式的好處減少頻繁創(chuàng)建,節(jié)省了。因此在單例模式必須包含三要素私有化構(gòu)造函數(shù),私有化。 單例作為一個(gè)最經(jīng)典的設(shè)計(jì)模式之一,到底什么是單例?為什么要用單例?怎么設(shè)計(jì)單例?php中單例如何具體實(shí)現(xiàn)? 一、什么是單例 wiki百科:單例模式,也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。 在應(yīng)用這個(gè)模式時(shí),單例對象的類必須...
摘要:上面是簡單的單例模式,自己寫程序的話夠用了,如果想繼續(xù)延伸,請傳送至大話設(shè)計(jì)模式之單例模式升級版 看了那么多單例的介紹,都是上來就說怎么做,也沒見說為什么這么做的。那小的就來說說為什么會(huì)有單例這個(gè)模式以便更好的幫助初學(xué)者真正的理解這個(gè)設(shè)計(jì)模式,如果你是大神,也不妨看完指正一下O(∩_∩)O首先我不得不吐槽一下這個(gè)模式名字單例,初學(xué)者通過字面很難理解什么是單例,我覺得應(yīng)該叫唯一模式更貼切...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進(jìn)行操作,第二類是返回一個(gè)容器接口對象,上節(jié)我們介紹了...
摘要:我們今天也來做一個(gè)萬能遙控器設(shè)計(jì)模式適配器模式將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。今天要介紹的仍然是創(chuàng)建型設(shè)計(jì)模式的一種建造者模式。設(shè)計(jì)模式的理論知識固然重要,但 計(jì)算機(jī)程序的思維邏輯 (54) - 剖析 Collections - 設(shè)計(jì)模式 上節(jié)我們提到,類 Collections 中大概有兩類功能,第一類是對容器接口對象進(jìn)行操作,第二類是返回一個(gè)容器接口對象,上節(jié)我們介紹了...
摘要:的設(shè)計(jì)模式有很多種,本文取最簡單的三種模式工廠模式單例模式和注冊樹模式進(jìn)行簡單的講解。文件創(chuàng)建完后,咱們回到單元測試文件文件再執(zhí)行一下單元測試命令發(fā)現(xiàn),也能返回成功,這樣的話我們就能很方便的修改任何驅(qū)動(dòng)了。 php 設(shè)計(jì)模式之工廠模式、單例模式、注冊樹模式 在軟件工程中,創(chuàng)建型設(shè)計(jì)模式承擔(dān)著對象創(chuàng)建的職責(zé),嘗試創(chuàng)建適合程序上下文的對象,對象創(chuàng)建設(shè)計(jì)模式的產(chǎn)生是由于軟件工程設(shè)計(jì)的問題,具...
閱讀 1754·2023-04-25 16:28
閱讀 689·2021-11-23 09:51
閱讀 1471·2019-08-30 15:54
閱讀 1155·2019-08-30 15:53
閱讀 2825·2019-08-30 15:53
閱讀 3419·2019-08-30 15:43
閱讀 3258·2019-08-30 11:18
閱讀 3273·2019-08-26 10:25