設計模式學習筆記 - 3
寫在前面
今日特餐:觀察者模式 (Observer)
先稍微複習上一篇筆記《設計模式學習筆記 - 2》的內容-策略模式:
可以定義和封裝一系列的演算法,並且讓它們是可替換/對調的。這個模式可以讓你在不影響用戶端的情況下獨立改變演算法。
我們透過冒險遊戲的角色及武器,對此模式有一定的認識與瞭解。
今天要學習的設計模式是觀察者模式,以下正文:
Observer 觀察者模式
發佈者 + 訂閱者 = 觀察者模式
觀察者模式定義務鍵之間的一對多依賴關係,當一個物件改變狀態時,依賴它的物件都會自動收到通知與更新。
可以想成youtuber 在影片的開頭或結尾,都會告訴觀眾,記得按讚、訂閱、開啟小鈴鐺,訂閱+開啟小鈴鐺,就可以在該youtuber 更新影片的時候,收到第一手的通知,而有訂閱的觀眾就成了觀察者。
為了確保每個youtuber 的行為,與每個觀眾的行為互相不干擾,所以在設計上會將youtuber 及 觀眾個別實做相同的介面:
youtuberInterface 會有訂閱、取消訂閱等方法,而AudienceInterface 會有接收通知的方法,確保不會因為每個youtuber 或觀眾不同,導致通知的時候出現問題。
根據上圖,我們透過簡單的程式碼來實做看看,其中我將上傳影片、取得訂閱者清單、增加訂閱者、取消訂閱等方法都直接放入介面中,是為了確定每個youtuber都是在相同的基礎行為上。
YoutuberInterface
1
2
3
4
5
6
7
8
9
10
11
Interface Youtuber {
public function register(Audience $audience);
public function remove(Audience $audience);
public function notifyAudience();
public function uploadVideo();
public function getAudienceList();
public function setAudience(Audience $audience);
public function removeAudience(Audience $audience);
}AudienceInterface
1
2
3
4
5
Interface Audience {
public function update(Youtuber $youtuber);
}YoutuberImplement
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
class YoutuberImpl implements Youtuber {
public $name;
private $audienceList = [];
public function __construct($name) {
$this->name = $name;
}
public function register(Audience $audience) {
echo 'You are an observer now!' . PHP_EOL;
$this->setAudience($audience);
echo 'there are ' . count($this->getAudienceList()) . ' audience !' . PHP_EOL;
}
public function remove(Audience $audience) {
echo 'You are no longer an observer!' . PHP_EOL;
$this->removeAudience($audience);
}
public function notifyAudience() {
$audienceList = $this->getAudienceList();
foreach ($audienceList as $audience) {
$audience->update($this);
}
}
public function uploadVideo() {
echo 'video upload successful!' . PHP_EOL;
$this->notifyAudience();
}
public function getAudienceList() {
return $this->audienceList;
}
public function setAudience(Audience $audience) {
$audienceList = $this->audienceList;
$audienceList[] = $audience;
$this->audienceList = $audienceList;
}
public function removeAudience(Audience $audience) {
$audienceList = $this->audienceList;
$newAudienceList = [];
foreach ($audienceList as $item) {
if ($audience != $item) {
$newAudienceList[] = $item;
}
}
$this->audienceList = $newAudienceList;
echo 'there are ' . count($this->getAudienceList()) . ' audience !' . PHP_EOL;
}
}AudienceImplement
1
2
3
4
5
6
7
class AudienceImpl implements Audience {
public function update(Youtuber $youtuber) {
echo $youtuber->name . ' has uploaded a new video!' . PHP_EOL;
}
}測試
測試的部分我們只操作一個頻道及一個觀眾,作為簡單的示例:
我們建立一個新的頻道叫做蔡阿嘎,以及一位觀眾Andy,
Andy 可以針對蔡阿嘎這個頻道進行訂閱、取消訂閱,而蔡阿嘎頻道可以上傳影片,並且通知訂閱者Andy。
1 |
|
思考
這邊例子僅簡單以echo 訊息的方式表示通知。未來可能要思考,通知的時候是傳遞哪些資料過去,傳遞所有資訊的話會不會造成觀察者的負擔。
再以書上的例子作為思考的出發點,要設計一個氣象站(Subject),每當天氣資訊更新的時候,就通知觀察者(Observer)們溫度、濕度、氣壓、降雨機率…等所有的天氣資訊。
但可能不是每個觀察者都需要所有的天氣資訊,比方說我每天出門前,我會在意的是今天的溫度及降雨機率,那麼其他資訊對我來說就是多餘的了。
所以可以調整為,天氣站要開放讓觀察者們自行取得需要的資料,而通知僅僅是告知他們,天氣資訊已經更新了。
個別的觀察者因為收到這則通知,自行向氣象站索取所需的資料:
getTemperature();
getHumidity();
…
以Youtube 的例子稍微說明,但實際的Youtube 應該做了更多更複雜的事情,
今天的筆記-觀察者模式到這邊告一段落,如果有謬誤的地方,歡迎email 給我指正或來信討論,謝謝!