模式對於面向對象開發是相當重要的。一種模式可以幫助我們創建能夠實現特定任務的對象,成為類的職責。模式還允許我們修改某個類,但不需要修改與這個類有關系的代碼,這個稱為類的多態。
單例模式又稱為職責模式,它用來在應用程序中創建一個單一的功能訪問點。下面我們來探討並且結結實實地掌握單例的思想還有應用。
在復雜的系統中,使用單例模式在維持應用程序狀態的同步方面尤其有用。所有的單例類至少擁有以下三個元素:
<?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');
也就是獲取對象的方法有些區別而已,使用起來與其它對象沒有特別之處。