0%

Design Pattern - Observer

Intro

這是一種訂閱機制,讓眾多訂閱者可以接收到變化的通知,當它們的訂閱對象發生改變的時候。

DOM 事件

我們常用的 addEventListener 也算是一種觀察者模式的實現。

1
2
const btnEl = document.querySelector('btn');
btnEl.addEventListener('click', e = > {...});

以上的範例,透過在特定按鈕上監聽特定的事件,因為我們不知道該綁定事件會在什麼時候被觸發,每當使用者點擊按鈕時,就會觸發該按鈕點擊事件綁定的 callback function。

真實事件 - 網站登錄

JavaScript 設計模式與開發實踐 一書中有提供一個實例,也就是接收登入者的資訊。
在網站中,頁面上各個元件基本都需要知道登入者的資訊,已得知該使用者有哪些權限,好判斷哪些內容可以被登入者檢視。

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
const login = {
evtList: {},
listen: function(evtName, fn) {...},
trigger: function() {
const key = Array.prototype.shift.call(arguments);
const fns = this.evtList[key];

for(let i = 0; i < fns.length; i++) {
fns[i].call(this, arguments);
}
}
}

fetch('https://xxx.com?login')
.then(res => res.json())
.then(res => {
login.trigger('loginSucc', res);
})

// --- 其他需要訂閱 loginSucc 事件的元件 ---
const avatar = (function() {
login.listen('loginSucc', function(data) {
header.setAvatar()
})

return {
setAvatar: function() {
console.log('time to set Avatar');
}
}
})()

const nav = (fnuction(){
login.listen('loginSucc', function(data) {
nav.displayOptions(data)
})

return {
displayOptions: function(data) {
console.log(`base on ${data} to filter options`);
}
}
})()

以上的內容就是在需要登入者資訊的元件中監聽特定事件 loginSucc,等到登入成功後,自動去觸發登錄在 login 裡的 loginSucc ,如此一來有監聽這個 loginSucc 事件的訂閱者們都會接受到登入者的資訊,再做後續的操作。

RxJS

在 Angular 框架裡面內建需要使用的 RxJS 套件,就有觀察者模式的機制的 api,
像是 BehaviorSubjectSubject 這兩個 api,就很常會用在當使用者成功登入之後,透過這些 api 來 broadcast 登入者的資料給有訂閱以上這兩個 api 的訂閱者。

Pros and Cons

pros:
我們可以減低訂閱者和被訂閱者之間的耦合程度

cons:
如果訂閱者太多,往後若發生 bug 時,在查錯的過程就會很耗時,因為需要去一個一個查訂閱者接收的內容是哪邊出錯了。

JavaScript 設計模式與開發實踐
https://refactoring.guru/design-patterns/observer