假設我們需要寫一個類用來操作資料庫,並同時滿足以下要求:
①SqlHelper類只能有一個執行個體(不能多)
②SqlHelper類必須能夠自行建立這個執行個體
③必須自行向整個系統提供這個執行個體,換句話說:多個對象共用一塊記憶體地區,比如,對象A設定了某些屬性值,則對象B,C也可以訪問這些屬性值(結尾的例子很好的說明了這個問題)
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)
那如何解決呢?
解決途徑:我們可以講getInstance()方法設為靜態,根據靜態定義,她只能被類而不是對象調用,將$_instance也設為靜態即可。所以這個方法正好符合我們的口味。
所以我們進一步將代碼修改如下:
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函數中對當前記憶體中有誤存在當類類的一個執行個體進行判斷,如果沒有則執行個體化,並返回物件控點,如果有則直接返回該物件控點
至此,完整代碼如下所示:
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();
資料庫名//$B->getDbName();
資料庫名//$C->getDbName();
也就是說,對象A,B,C實際上都是使用同一個對象執行個體,訪問的都是同一塊記憶體地區
所以,即使unset($A),對象B和C還是照樣能夠通過getDbName()方法輸出“資料庫名”的
unset($A)實際上只是將對象A與某塊記憶體位址(該對象的執行個體所在的地址)之間的連絡方式斷開而已,跟對象B和對象C無關,可以用用一張圖表示如下
原創文章:MarcoFly
轉載請註明出處:http://www.cnblogs.com/hongfei/archive/2012/07/07/2580994.html