我就是我,獨一無二的我。
設計模式是解決開發時遇到普遍存在(反覆出現)的問題的各種解法。但並不是絕對的,遇到問題才使用解法而不是為了使用而使用。
切記: 不要拿了錘子,看什麼都是釘子
介紹
單例模式屬於創建型模式,目的是保證一個類別只會產生一個物件,而且要提供存取該物件的統一方法。
情境
哈利
與鄧不利多
分別攻擊伏地魔
,而伏地魔
應該要因為攻擊而被層層削弱。
錯誤範例
哈利、鄧不利多和伏地魔的物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| # 伏地魔 class Voldemort { private $blood = 7;
/** * 扣除血量 * @param int $del */ public function deduct(int $del) { $this->blood = $this->blood - $del; }
/** * 顯示血量 * @return int */ public function show() { return $this->blood; } }
# 鄧不利多 class Dumbledore { public function attack() { $voldemort = new Voldemort();
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; echo '鄧不利多攻擊 ' . '<br />';
$voldemort->deduct(3);
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; } }
# 哈利 class Harry { public function attack() { $voldemort = new Voldemort();
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; echo '哈利波特攻擊 ' . '<br />';
$voldemort->deduct(1);
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; } }
|
揍他
1 2 3 4 5 6 7 8 9
| $Dumbledore = new Dumbledore(); $Harry = new Harry();
echo '鄧不利多攻擊 <br />'; $Dumbledore->attack();
echo '<br />'; echo '哈利波特攻擊 <br />'; $Harry->attack();
|
結果
1 2 3 4 5 6 7 8 9
| 鄧不利多攻擊 伏地魔剩餘分靈體數量: 7 鄧不利多攻擊 伏地魔剩餘分靈體數量: 4
哈利波特攻擊 伏地魔剩餘分靈體數量: 7 哈利波特攻擊 伏地魔剩餘分靈體數量: 6
|
咦!!!為什麼大魔王打不死!?
那是因為哈利跟老鄧分別實例化了自己的佛地魔。攻擊當然沒有串連起來囉。
正確範例
讓我們套用單例
調整佛地魔的實例化方式。
哈利、鄧不利多和伏地魔的物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| # 伏地魔 class Voldemort { private static $instance; private $blood = 7;
private function __construct() { # 建構方式虛擬化,確保無法從外部實例物件 }
/** * 取得伏地魔物件 * @return Voldemort */ public static function getInstance() { if (!self::$instance) { self::$instance = new self(); }
return self::$instance; }
/** * 扣除血量 * @param int $del */ public function deduct(int $del) { $this->blood = $this->blood - $del; }
/** * 顯示血量 * @return int */ public function show() { return $this->blood; } }
# 鄧不利多 class Dumbledore { public function attack() { $voldemort = Voldemort::getInstance();
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; echo '鄧不利多攻擊 ' . '<br />';
$voldemort->deduct(3);
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; } }
# 哈利 class Harry { public function attack() { $voldemort = Voldemort::getInstance();
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; echo '哈利波特攻擊 ' . '<br />';
$voldemort->deduct(1);
echo '伏地魔剩餘分靈體數量: ' . $voldemort->show() . '<br />'; } }
|
- 將
伏地魔
建構元私有化,確保其他人無法從外部實例化。
- 建立
instance
實例機制,讓物件永遠保持單一。
再來,揍他
1 2 3 4 5 6 7 8 9
| $Dumbledore = new Dumbledore(); $Harry = new Harry();
echo '鄧不利多攻擊 <br />'; $Dumbledore->attack();
echo '<br />'; echo '哈利波特攻擊 <br />'; $Harry->attack();
|
結果
鄧不利多攻擊
伏地魔剩餘分靈體數量: 7
鄧不利多攻擊
伏地魔剩餘分靈體數量: 4
哈利波特攻擊
伏地魔剩餘分靈體數量: 4
哈利波特攻擊
伏地魔剩餘分靈體數量: 3
討論
此處筆者僅將佛地魔物件當成單例
範例,其他範例可以想像倉庫與送貨員,班導師與學生之間的關係。
單例模式能確保共同物件的單一使用及系統效能的優化。算是簡單好用的設計模式。
許多知名的 Framework 底層也有實作此模式,有興趣的可以多加參考。