Ch3 事件導向架構 (Event-Based Architectures) | 可測試的 JavaScript (Testable JavaScript)
27 Apr 2021JavaScript 是「事件導向」的程式語言
如下圖,使用者與 UI (在瀏覽器內) 互動,瀏覽器即是 event listener,負責做監聽動作。因此當使用者做了一些事情後,瀏覽器便會通知特定的監聽者做出反應。
緊密耦合
- 發生原因:緊密耦合的起因是由於物件的溝通是靠 (1)直接參考 (direct reference) 來呼叫方法 (method) 或 (2) 設定屬性 來接觸物件,這樣的方式會導致物件彼此緊密耦合,例如:(1) 共用耦合:物件 A 與 B 的有效範圍是全域的 (global),很容易被更動、誤改;(2) 控制耦合:透過函式參數傳遞或注入;(3) 資料耦合:區域性範圍實體化特定物件。
- 解法:在「事件導向」的架構中,傳遞訊息給物件的作法改為「間接參考」的方式,也就是物件彼此不直接聯繫,而是透過 event listener 做傳遞,這樣物件彼此就沒有關聯。如下,A 與 B 彼此無直接關聯,而是透過 event listener 來溝通。
事件集線器 (Event Hub)
如上圖,方法 1 ~ 5 對事件集線器註冊,當有物件呼叫的時候,事件集線器監聽到有人呼叫,就分配對應的方法來做處理。
這樣利用物件呼叫傳入 callback 的方式來呼叫方法,就無共享變數的狀況,也就沒有緊密耦合的問題。
回應丟出的事件
事件集線器回應的三種方式
- 不做回應:只丟出事件。
- 普通回應:監聽特定事件。
- 特定回應:監聽特定事件,並且直接送回應給事件丟出者,意即將 callback 參數傳入事件集線器。
事件導向架構與 MVC 作法
不同點在於
- 事件導向架構的 model 單純存資料,再由事件集線器傳遞資料,優點是較好測試、有彈性。
- MVC 作法的 model 含資料與方法,容易外顯和共用,耦合度高,難測試、較無彈性。
事件導向架構與物件導向程式設計 (OOP)
OOP 有繼承耦合的問題
- 程式以繼承 (inheritance) 或介面 (interface) 做連結,可類比為 JavaScript 中的 prototype。
- 資料共用。
相較事件導向架構,由於元件都是 private,且唯一的溝通管道是透過事件集線器的 API,無耦合問題。
事件導向架構與軟體即服務 (SaaS)
在 SaaS 環境中,適合使用事件導向架構
- SaaS 的每個片段(下圖中的小圈圈)可以加入事件集線器,以獨立提供服務。
Web 應用程式
- web server 負擔太大,因此不該作為事件集線器,應使用 web socket、socket.io 取代。
- same-origin -> 改用 HTML5 的 postMessage、socket.io。
測試事件導向架構
呼叫實作動作的函式
事件導向架構注意事項
- 擴展性:load balance 加上事件集線器。
- 廣播:不要直接全送,不然流量上升。
- 執行期確認:避免人為疏失。
- 安全性:集線器需要限定連接的用戶 (auth)。
- 狀態:信任的 server 可取得 session key;不信任的 server 使用 hash field。
較聰明的集線器:事件交換器
- 單播 (unicast)。
- 廣播 (broadcast)。
節省網路頻寬、程式部署。
佈署
升級應用程式的一部份,只需要更新和部署單一模組,不漏失處理任何要替換的事件。
Event Hub + Jest
本章重點回顧
- 事件導向架構的優點:高模組化、鬆散偶和、易重用,因此好測試。
- 事件集線器。
- 利用物件呼叫傳入 callback 的方式來呼叫方法,無共享變數的狀況,也就沒有緊密耦合的問題。
- 推廣 SaaS。
- 事件交換器,除了以上優點,並且更好部署。
其他補充
- tiny-emitter
- tape: An unit test framework, just like jest 😎
- 訂閱後只執行一次 :unicast eventhub once、jquery once