Design Pattern | 只要你想知道,我就告訴你 - 觀察者模式( Observer Pattern ) feat. TypeScript
前言
Hi!大家好,我是神 Q 超人。不曉得上一篇的 策略模式 大家感覺如何,就筆者來說,還滿喜歡看 Design Pattern 的,因為它著重在解決問題的方式,我不能說學了 Design Pattern 後,每次打出來的程式碼都超級完美,但是它一定能夠讓你在某個 coding 的 moment 感受到
我也許可以這樣寫?
當這個念頭一閃而過的時候,別懷疑, Do 下去就對了,像五月天教過我們的,
每次衝動留下的 都有所不同
然而有天你會懂 就是那些 讓你不同
那為了讓大家衝動的次數和可能性能夠變多,本文就來提提新的 Design Pattern 。
Observer Pattern (觀察者模式)
當多個 Class 都需要接收同一種資料的變化時,就適合使用 Observer Pattern
上方「多個 Class 」指的就是「觀察者」,而「同一種資料」指的就是觀察者們想了解的「主題」,因此
Observer Pattern 實作的原理就是把獲取資料的部分抽離出來,並在資料改變時,同步送給所有的觀察者。
且觀察者可以在任何時候決定是否要繼續接收資料。
以 Youtuber 來說,他們會在所有影片的最後或一開始告訴大家說:「如果喜歡我的影片,記得幫我按讚分享,別忘了開啟小鈴鐺訂閱我哦!」。
這個訂閱的功能就像「主題」與「觀察者」之間的關係,只要你開啟訂閱,系統就會把你加進「訂閱者」的清單,在新影片發佈時,會將通知送給清單內的人,當然如果有一天,訂閱者不再喜歡這些影片,也可以隨時取消訂閱,只要被移出「訂閱者」清單,就不會再收到通知。
Observer Pattern 實作流程
以下用幾個流程來完成一個簡單的 Observer Pattern 。
找出「主題」和「觀察者們」
就上方的例子來說,「 Youtuber 」就是主題,「喜歡影片而訂閱的人 」就是觀察者。
設計觀察者的 Interface
這裡先設計 Interface 有幾個原因:
- 因為觀察者不只一個,為了讓所有的觀察者都能夠被主題「加入訂閱」和「移除訂閱」,它們就得擁有同一個 Interface ,當執行事件時,就只需要以 Interfacet 做共同型別代替 Class 傳入。
- 觀察者需要被通知,用 Interface 可以確保所有 Class 都有接受通知的行為存在。
雖然感覺很複雜,但是實際上,在觀察者的 Interface 內,只需要確保有接受通知的行為就可以了:
設計主題的 Interface
主題是 Youtuber ,它會需要三個行為,分別是「加入訂閱」、「移除訂閱」、「通知訂閱的人」:
這裡就可以看見 IObserver
的用途了,如果所有的訂閱者沒有同一個 Interface ,那這裡就會變得很複雜,更詳細的可以參照 這篇文章 內提到關於 Interface 的應用。
以 IYoutuber 實作 Class
上方不斷地提到「訂閱」這個功能,但是究竟要把訂閱的人保存在哪裡呢?就直接放到 Class 的 Private 屬性中吧!當然這個屬性的型別是 Array 陣列,而 Array 內存放的型別就是 IObserver
:
接下來,關於「訂閱」和「取消訂閱」這兩個功能,就是將送進來的參數加入或從陣列移除,而負責送通知給訂閱者的 notifyObserver
,則是用 for
將 observers
內的所有訂閱者都取出來,執行 update
:
上方的 notifyObserver
也因為 observers
內的型別都是 IObserver
,所以一定會有 update
能執行, Interface 真的太猛了。
目前的 Youtuber
內有了幾個主要的行為,但這樣有點不夠真實,因此我再為他們加上了 name
和 publishVideo
,待會就可以創建各個不同的 Youtuber 然後透過 publishVideo
假裝發佈新影片,然後用 notifyObserver
做通知,完成後的 Youtuber
會長這樣:
以 IObserver 實作 Observer
這裡比較輕鬆,只需要確認 Observer
能夠有 update
就好,其他事情則是在 update
處理:
完成 Youtuber
和 Observer
後,就能來看看怎麼使用它們:
執行結果如下:
如果取消訂閱,不再接收任何通知也非常簡單,只需調用 removeObservers
:
從執行結果可以看到取消訂閱後,當 aGa
再執行 publishVideo
時,observer
就不會得到通知,因為 observer
已經不在 aGa
內部的訂閱者清單中了:
結論
- Interface 真的很偉大。
- 將負責處理資料的部分抽到
Youtuber
,當有新影片就主動通知所有訂閱的人,而不是由訂閱人一直問說有沒有新影片了。 - 耦合度很低,因為只要是實作了
IObserver
接口都可以接收新影片,而不是特定某個 Class 或是 Object 做型別參數,傳入了才發現沒有update
的行為。 Youtuber
不管誰收到通知後要幹嘛,因為它只負責通知,上方是以觀影者的角度接收通知,但其實不只觀影者,就算是官方負責統計資料的 Class ,只要它有實作IObserver
接口,那就能和觀影者同步接收到新影片的通知,各自處理接到資料後的邏輯。
原本以為 Observer Pattern 會很容易說明,結果發現真的很困難啊 😭 ,尤其是在解說例子的部分,如果對文章有任何建議、問題、或是需要改進的地方再麻煩各位留言告訴我,謝謝大家!
參考文章