設計模式 - 觀察者模式

想要我的財寶嗎?想要的話可以全部給你,自己去找吧!我把所有財寶都放在那裡。

不用這麼麻煩!訂閱我,最新的財寶資訊將會主動送到您手上!

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

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

介紹

觀察者屬於行為模式,定義物件間的一對多的依賴關系,當一個被依賴物件的狀態發生改變時,所有依賴於它的物件都被自動更新

以 Youtuber 來說,他們會邀請觀看者訂閱小鈴鐺,這個訂閱功能就像「主題」與「觀察者」之間的關係,只要你開啟訂閱,在新影片發布時,系統會自動通知你。當然「觀察者」也可以隨時退訂。

組成

  • 被觀察者(Observable): 定義了訂閱取消訂閱更新通知
  • 觀察者(Observer): 定義更新狀態方法,供被通知者進行通知行為

情境

哥爾羅傑白鬍子為了提高自己的知名度,會不定時放送自己的財寶資訊。讓各位海賊們觀看。

部分鐵粉海賊會進行訂閱自己的支持者以免錯過訊息。其中不乏兩位都訂閱的海賊。

範例

定義介面

1
2
3
4
5
6
7
8
9
10
11
# 觀察者
interface IObserver {
public function update();
}

# 抽象被觀察者
interface IObservable {
public function add(IObserver $observer);
public function remove(IObserver $observer);
public function notify();
}

角色實作

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
# 大海賊(被觀察者)
class BigPirate implements IObservable {
protected string $name;

protected array $observerAry=[];

public function __construct(string $name)
{
$this->name = $name;
}

/**
* 訂閱
* @param IObserver $observer
*/
public function add(IObserver $observer)
{
if (!in_array($observer, $this->observerAry)) {
$this->observerAry[] = $observer;

echo '已訂閱' . $this->name . '<br/>';
}
}

/**
* 取消訂閱
* @param IObserver $observer
*/
public function remove(IObserver $observer)
{
if (in_array($observer, $this->observerAry)) {
$key = array_search($observer, $this->observerAry);
unset($this->observerAry[$key]);

echo '已取消訂閱' . $this->name . '<br/>';
}
}

/**
* 通知更新
*/
public function notify()
{
foreach ($this->observerAry as $observer) {
$observer->update($this->name . '已發布最新寶藏資訊');
}
}
}

# 海賊(觀察者)
class Pirate implements IObserver {
protected string $name;

public function __construct(string $name)
{
$this->name = $name;
}

public function update(string $notice)
{
echo '給' . $this->name . ',' . $notice . '<br/>';
}
}

來看看魯來訂閱兩位大海賊,又取消訂閱白鬍子後。大海賊發新片的狀況吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 大海賊們
$roger = new BigPirate('哥爾羅傑');
$edward = new BigPirate('白鬍子');

# 海賊
$luffy = new Pirate('魯夫');

# 訂閱
$roger->add($luffy);
$edward->add($luffy);

# 取消訂閱
$edward->remove($luffy);

# 哥爾羅傑發新片
$roger->notify();

# 白鬍子發新片
$edward->notify();

結果

1
2
3
4
已訂閱哥爾羅傑
已訂閱白鬍子
已取消訂閱白鬍子
給魯夫,哥爾羅傑已發布最新寶藏資訊

討論

觀察者模式可有效解除關聯物件間的高耦合問題,只要正確實作 IObserver (觀察者) 介面就可以對有興趣的主題進行訂閱行為。而透過介面的規範可以確保實作出來的觀察者具備必要的方法函示。

觀察者模式下觀察者可透過訂閱避免頻繁向被觀察者查詢是否有新作品的問題。

Author

LinYoYo

Posted on

2022-01-12

Updated on

2022-01-12

Licensed under