面向對象 - 設計模式 - 單例模式

問題

  • 經過良好設計的系統一般通過方法調用來傳遞對象實例。每個類都會與背景環境保持獨立,並通過清晰的通訊方式來與系統中其他部分進行協作。有時你需要使用一些作爲對象間溝通渠道的類,此時就不得不引入依賴關係。
  • 假設有一個用於保存應用程序信息的 Preferences 類。我們可能會使用一個 Perferences 對象來保存諸如DSN(用於保存數據庫的表及用戶信息)字符串,URL根目錄、文件路徑等數據。這些信息在你每次部署程序時都可能會有所不同。改對象也可被用作一個“公告板”,它是可以被系統中其他無關對象設置和獲取消息的中心。
  • 但在對象中傳遞 Preferences 對象並不總是個好主意。你可以讓原來並不使用 Preferences 對象的類強制性地接受 Preferences 對象,以便這些類能傳遞 Preferences 對象給其它對象,但這樣做產生了另一種形式的耦合。
  • 我們需要保證系統中的所有對象都使用同一個 Preferences 對象。我們不希望一些對象在一個 Preferences 對象上設值,而其它對象從另外一個完全不同的 Preferences 對象上讀取數據。
  • 讓我們提煉出這個問題的幾個關鍵點:
    1. Preferences 對象應該可以被系統中的任何對象使用
    2. Preferences 對象不應該被存儲在會被覆寫的全局變量中。
    3. 系統中不應超過一個 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 屬性設置爲 privatestatic ,因此不能再類外部被訪問。而 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類圖
    單例模式UML類圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章