PHP中」單例模式「實例講解

原文地址:http://www.cnblogs.com/hongfei/archive/2012/07/07/2580994.htmlphp

 

假設咱們須要寫一個類用來操做數據庫,並同時知足如下要求:html

①SqlHelper類只能有一個實例(不能多)
②SqlHelper類必須可以自行建立這個實例
③必須自行向整個系統提供這個實例,換句話說:多個對象共享一塊內存區域,好比,對象A設置了某些屬性值,則對象B,C也能夠訪問這些屬性值(結尾的例子很好的說明了這個問題)sql

複製代碼

 1 <?php 2     class SqlHelper{ 3         private static $_instance; 4         public $_dbname; 5         private function __construct(){ 6              7         } 8         public function getDbName(){ 9             echo $this->_dbname;10         }11         public function setDbName($dbname){12             $this->_dbname=$dbname;13         }14         public function clear(){15             unset($this->_dbname);16         }17         18     }19     $sqlHelper=new SqlHelper();//打印:Fatal error: Call to private SqlHelper::__construct() from invalid context 20 ?>

複製代碼

以上的SqlHelper類是沒法從自身的類外部建立實例的,由於咱們將構造函數設爲了private,因此經過new SqlHelper()是沒法從類外部使用私有的構造函數的,若是強制使用,將會報以下錯誤:
Fatal error: Call to private SqlHelper::__construct() from invalid context
嚴重錯誤:從上下文中調用了一個私有的構造函數SqlHelper::__construct()數據庫

按照已往的思惟邏輯,實例化一個類都是直接在類外部使用new操做符的,可是既然這裏講構造函數設爲private了,咱們知道,私有的成員屬性或函數只能在類的內部被訪問,因此咱們能夠經過在類SqlHelper內部再建立一個函數(好比:getInstance()),並且必須是public的,getInstance()函數中主要進行的是實例化SqlHelper類
好比:函數

複製代碼

 1 <?php 2     class SqlHelper{ 3         private $_instance; 4         //......省略 5         public function getInstance(){ 6             $this->_instance=new SqlHelper(); 7         } 8         //......省略 9     }10 ?>

複製代碼

可是問題出現了,
①咱們在調用getInstance()以前沒有實例化SqlHelper對象,因此也就沒法經過對象的方式來調用getInstance()函數了,
②既然在調用getInstance的時候還未實例化出對象,因此在getInstance函數中使用$this確定也會報錯(Fatal error: Using $this when not in object context)
那如何解決呢?this

解決途徑:咱們能夠講getInstance()方法設爲靜態的,根據靜態的定義,她只能被類而不是對象調用,將$_instance也設爲靜態的便可。因此這個方法正好符合咱們的口味。
因此咱們進一步將代碼修改以下:spa

複製代碼

 1 <?php 2     class SqlHelper{ 3         private static $_instance; 4         private function __construct(){ 5             echo "構造函數被調用"; 6         }        
 7         //......省略 8         public static function getInstance(){ 9             if (self::$_instance===null) {10 //                self::$_instance=new SqlHelper();//方式一11                 self::$_instance=new self();//方式二                12             }13             return self::$_instance;14         }15         //......省略16     }17     $sqlHelper=SqlHelper::getInstance();//打印:構造函數被調用18 ?>

複製代碼

經過在getInstance函數中對當前內存中有誤存在當類類的一個實例進行判斷,若是沒有則實例化,並返回對象句柄,若是有則直接返回該對象句柄
至此,完整代碼以下所示:code

複製代碼

 1 <?php 2     class SqlHelper{ 3         private static $_instance; 4         public $_dbname; 5         private function __construct(){ 6              7         } 8         //getInstance()方法必須設置爲公有的,必須調用此方法 9         public static function getInstance(){10             //對象方法不能訪問普通的對象屬性,因此$_instance須要設爲靜態的11             if (self::$_instance===null) {12 //                self::$_instance=new SqlHelper();//方式一    13                 self::$_instance=new self();//方式二        14             }15             return self::$_instance;16         }17         public function getDbName(){18             echo $this->_dbname;19         }20         public function setDbName($dbname){21             $this->_dbname=$dbname;22         }23     }24 //    $sqlHelper=new SqlHelper();//打印:Fatal error: Call to private SqlHelper::__construct() from invalid context 25     $A=SqlHelper::getInstance();26     $A->setDbName('數據庫名');27     $A->getDbName();28 //    unset($A);//移除引用29     $B=SqlHelper::getInstance();30     $B->getDbName();31     $C=SqlHelper::getInstance();32     $C->getDbName();33     34 ?>

複製代碼

以上代碼的執行結果:
數據庫名//$A->getDbName();htm

數據庫名//$B->getDbName();
數據庫名//$C->getDbName();
也就是說,對象A,B,C實際上都是使用同一個對象實例,訪問的都是同一塊內存區域
因此,即便unset($A),對象B和C仍是照樣可以經過getDbName()方法輸出「數據庫名」的
unset($A)實際上只是將對象A與某塊內存地址(該對象的實例所在的地址)之間的聯繫方式斷開而已,跟對象B和對象C無關,能夠用用一張圖表示以下對象

 

相關文章
相關標籤/搜索