設計模式 - 單例模式

我就是我,獨一無二的我。

設計模式是解決開發時遇到普遍存在(反覆出現)的問題的各種解法。但並不是絕對的,遇到問題才使用解法而不是為了使用而使用

切記: 不要拿了錘子,看什麼都是釘子

介紹

單例模式屬於創建型模式,目的是保證一個類別只會產生一個物件,而且要提供存取該物件的統一方法。

情境

哈利鄧不利多分別攻擊伏地魔,而伏地魔應該要因為攻擊而被層層削弱。

錯誤範例

哈利、鄧不利多和伏地魔的物件。

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 底層也有實作此模式,有興趣的可以多加參考。

Author

LinYoYo

Posted on

2022-01-21

Updated on

2022-01-21

Licensed under