模式對於面向對象開發是相當重要的。一種模式可以幫助我們創建能夠實現特定任務的對象,成為類的職責。模式還允許我們修改某個類,但不需要修改與這個類有關系的代碼,這個稱為類的多態。
單例模式又稱為職責模式,它用來在應用程序中創建一個單一的功能訪問點。下面我們來探討並且結結實實地掌握單例的思想還有應用。
在復雜的系統中,使用單例模式在維持應用程序狀態的同步方面尤其有用。所有的單例類至少擁有以下三個元素:
<?php class Fruit { // Hold an instance of the class private static $instance; // A private constructor; prevents direct creation of object // 防止類被當作實例使用,就是無法使用此類創建對象 private function __construct() { echo 'I am constructed'; } // The singleton method public static function singleton() { if (!isset(self::$instance)) { $c = __CLASS__; self::$instance = new $c; } return self::$instance; } // Example method public function showColor() { echo 'My color is !'; } // Prevent users to clone the instance public function __clone() { trigger_error('Clone is not allowed.', E_USER_ERROR); } } // This would fail because the constructor is private //$test = new Fruit(); // This will always retrieve a single instance of the class $test = Fruit::singleton(); echo '<br />'; $test->showColor(); // This will issue an E_USER_ERROR. //$test_clone = clone $test; ?>
程序運行結果:
I am constructed My color is !
從這個程序可以看出一些特別的東西。和普通的類不同,單例類是不能直接實例化的,它只能被自身實例化。要獲得這種效果,__construct()方法必須被標記為private。如果試圖用private構造函數構造一個對象,就會得到一個訪問性級別的錯誤。
那麼單例類如何起作用呢?單例類就是要向其它類提供一個實例,用它調用各種方法。單例類回通過內部存儲的實例返回一個引用,所以單例類不會重復占用內存和系統資源,從而讓應用程序的其它部分更好的使用資源。所以,你的數據庫訪問最好使用單例模式構建,那麼就不會創建太多的數據庫連接實例,從而讓你的系統跑得更快。
一個空的__clone()方法很有必要,它可以防止對象被復制或者克隆。
self::$instance 可以檢測到類是否已經被初始化。如果保存實例的靜態成員為空或者還不是類自身的一個實例,那麼這個實例將會被創建並保存到存放實例的變量中。
一個不嚴格的單例,沒有private構造函數,也沒有本身的引用。不知道還算不算模式了。
<?php class Fruit { public static $height = 2; public static $weight = 2; public static function getInstance() { return new Fruit(); } public function getHeight() { return self::$height; } public function getWeight() { return self::$weight; } public function setHeight($value) { if($value > 0 && $value < 100) self::$height = $value; } public function setWeight($value) { if($value > 0 && $value < 100) self::$weight = $value; } public function __toString() { return 'Fruit[height=' . self::$height . ', weight=' . self::$weight . ']'; } } // try to set data before any objects is created Fruit::$height = 55; $msm1 = Fruit::getInstance(); // use the getInstance() method $msm2 = new Fruit(); // use the default constructor $msm2->setWeight(78); // set data with an instantiated object echo $msm1 . '<br />'; echo $msm2 . '<br />'; echo Fruit::getInstance() . '<br>'; echo (new Fruit()); ?>
程序運行結果:
Fruit[height=55, weight=78] Fruit[height=55, weight=78] Fruit[height=55, weight=78] Fruit[height=55, weight=78]
<?php class Database { private $_db; static $_instance; private function __construct() { $this->_db = pg_connect('dbname=example_db'); } private __clone() {}; public static function getInstance() { if( ! (self::$_instance instanceof self) ) { self::$_instance = new self(); } return self::$_instance; } public function query($sql) { return pg_query($this->_db,$sql); } } ?>
如何使用這個單例類?
$db = Database::getInstance(); $db->query('SELECT * FROM example_table');
也就是獲取對象的方法有些區別而已,使用起來與其它對象沒有特別之處。