在 PHP 中,甚至不僅 PHP 中,咱們都會用到全局變量,以保存全局狀態。但是,每每全局變量是全局共享的,任何地方任何代碼都有可能將其覆蓋。例如,咱們定義一個全局變量叫作 PHONE。咱們在某一行代碼中,將其定義成了 iPhone,可是咱們不當心在另外一行代碼中將其覆寫成了 Nokia。這就很是的尷尬了,由於原本咱們並不想它被覆寫。php
在一個系統中,咱們會定義許多的方法,生成不少的對象。有時候,咱們會使用不少的方法,對同一個對象作操做。在不使用全局變量的狀況下,咱們須要將對象做爲參數傳入方法中。可是這樣傳遞同一個對象,可能會形成混亂,還可能形成沒必要要的依賴。設計模式
其實咱們只須要一個全局可訪問的對象就能夠解決這個,可是全局變量又會出現咱們上面的說的問題。函數
咱們要解決這些問題,咱們對這樣的對象有下面的幾個目標。學習
以上的幾個目標,就是咱們所須要的,也就是單例模式的特徵。this
class Preference { private static $instance; private $props = []; private __construct() {} public static function getInstance() { if (empty(self::$instance)) { self::$instance = new Preference(); } return self::$instance; } public function setProperty($key, $value) { $this->props[$key] = $value; } public function getProperty($key) { return $this->props[$key]; } private function __clone() {} private function __sleep() {} private function __wakeup() {} }
咱們在這裏引入了一個私有的構造函數,這樣,外部就沒法實例化這個對象了。同時,咱們使用 getInstance
方法來獲取具體的實例,而沒法去覆寫它,這就達成了第二個目標。spa
因爲 $instance
和 getInstance
都是靜態的,因此咱們能夠經過 Preference::getInstance()
訪問,具體的實例。這樣就使得全局均可以訪問到它了,它就像全局變量同樣了,這就達成了第一個目標了。設計
對於這個類,咱們沒法生成第二個對象,由於它的構造函數是私有的,而且 __clone
方法是私有的,並且,getInstance
在判斷已經有了一個實例的狀況下默認返回該實例。這就達成了第三個目標了。code
同時,咱們也儘可能避免序列化這個實例,因此咱們給 __wakeup
和 __sleep
這兩個魔術方法私有。對象
這就是單例模式。rem
對於單例模式,其實沒有那麼高大上。只不過是更改的對象的訪問範圍,以及對象始終存在,僅此而已。
最後,本文章是做者在學習設計模式時的感想。部分參考自《深刻 PHP 面向對象、模式與實踐(第 3 版)》。若有錯誤,感謝大神不吝賜教。