問題
- 經過良好設計的系統一般通過方法調用來傳遞對象實例。每個類都會與背景環境保持獨立,並通過清晰的通訊方式來與系統中其他部分進行協作。有時你需要使用一些作爲對象間溝通渠道的類,此時就不得不引入依賴關係。
- 假設有一個用於保存應用程序信息的
Preferences
類。我們可能會使用一個 Perferences 對象來保存諸如DSN(用於保存數據庫的表及用戶信息)字符串,URL根目錄、文件路徑等數據。這些信息在你每次部署程序時都可能會有所不同。改對象也可被用作一個“公告板”,它是可以被系統中其他無關對象設置和獲取消息的中心。 - 但在對象中傳遞 Preferences 對象並不總是個好主意。你可以讓原來並不使用 Preferences 對象的類強制性地接受 Preferences 對象,以便這些類能傳遞 Preferences 對象給其它對象,但這樣做產生了另一種形式的耦合。
- 我們需要保證系統中的所有對象都使用同一個 Preferences 對象。我們不希望一些對象在一個 Preferences 對象上設值,而其它對象從另外一個完全不同的 Preferences 對象上讀取數據。
- 讓我們提煉出這個問題的幾個關鍵點:
- Preferences 對象應該可以被系統中的任何對象使用
- Preferences 對象不應該被存儲在會被覆寫的全局變量中。
- 系統中不應超過一個 Preferences 對象。也就是說,Y對象可以設置 Preferences 對象的一個屬性,而Z對象不需要通過其它對象(假設Y和Z都可以訪問 Preferences 對象)就可以直接獲取該屬性的值。
實現
- 使用靜態方法和靜態屬性來間接實例化對象。
class Preferences {
private $props = array();
private static $instance = null;
/**
* 構造函數
*/
private function __construct()
{
}
/**
* 不允許深度複製
*/
private function __clone()
{
}
/**
* 不允許serialize
*/
private function __sleep()
{
}
/**
* 不允許unserialize
*/
private function __wakeup()
{
}
/**
* 獲取單例
* @return 實例化後的對象
*/
public static function getInstance()
{
if (empty(self::$instance)) {
self::$instance = new Preferences();
}
return self::$instance;
}
/**
* 設置屬性值
* @param string $key 鍵
* @param string $val 值
* @return boolen 布爾值
*/
public function setProperty($key, $val)
{
$this->props[$key] = $val;
return true;
}
/**
* 獲取屬性值
* @param string $key 鍵
* @return $this->props 屬性值
*/
public function getProperty($key = null)
{
if (is_null($key)) {
return $this->props;
} else {
return $this->props[$key];
}
}
}
- $instance 屬性設置爲
private
及static
,因此不能再類外部被訪問。而 getInstance() 方法是 public 且 static 的,所有在腳本的任何地方都可被調用。
$pref = Preferences::getInstance();
$pref->setProperty('subject', 'php');
unset($pref); // 移除引用
$pref2 = Preferences::getInstance();
$resp = $pref2->getProperty('subject');
var_dump($resp); //該屬性值並沒有丟失
response:php
靜態方法不能訪問普通的對象屬性,因爲根據靜態的定義,它只能被類而不是對象調用。但靜態方法可以訪問靜態屬性。所以當 getInstance() 被調用時,我們會檢查
Preference::$instance
屬性。如果爲空,那麼創建一個 Preferences 對象實例並把它保存在$instance
屬性中,然後我們把實例返回給調用代碼。因爲靜態方法 getInstance() 是 Preferences 類的一部分,所以儘管構造方法是私有的,但是實例化 Preferences 對象完全沒有問題。單例模式UML類圖