前一篇 Observer Pattern 觀察者模式 提到一些可以透過 Delegate 與 Event 克服的問題,
最主要去克服的概念就是 " Subject 通知者 與 Observer 觀察者 耦合在一起" ,
白話一點要克服的就是" Subject 的視野必須有 Observer ,Observer 的視野也必須有 Subject "。
這篇就來示範如何操作 Delegate 與 Event 來解耦,程式碼請參考 Chapter_14.2。
先來看看如果我們使用 Delegate 與 Event 的話,專案省去了哪些東西。
可以發現使用了Delegate之後(右邊Chapter_14.2),便不需要 Subject 與 Observer 介面了,
自然就沒有兩者耦合與能不能夠實作介面的問題。
情境
每當 Boss 回到辦公室的時候,都習慣會說"我回來了",佛心老闆,
而摸魚的兩位 employee 每次只要聽到老闆說"我回來了",就會把眼前的影片關掉。
由於這個說的人可能是 Boss,也有可能是好心的 Secretary 通風報信,
上述的"說"就是通知者發出通知的動作,
"關掉"就是觀察者做出回應的動作。
兩種做法
這次除了Delegate之外,也想使用微軟已經定義好的 EventHandler 進行示範,
所以先拿 Boss 來實作 Delegate 與 Event,後面用 Secretary 類別說明什麼是 EventHandler。
在建立Event之前,先建立要傳遞的參數類別
這一次的情境中,我們的 通知者 是會挾帶訊息的,因此 Event 必須透過 EventArgs 挾帶訊息,
首先在每個檔案都看的到的地方定義一個類別作為事件參數,這次是和 Boss 類別放在一起,
類別中定義了要挾帶的資料與型態,且這個類別必須繼承 EventArgs。
我在這個 Event 會傳遞的參數類別中只定義一個 String 型別資料,
接著來看Event要如何傳遞它。
實作 Delegate 與 Event
首先先定義 Delegate 型別,後方的參數規範了要被委託的 function 應有的格式。
這邊宣告了一個 Delegate 型別 名為 NotifyEventHandler,參數有兩個,
分別是 object 與 notifyObserverEventArgs 型別,
因此要託付給 NotifyEventHandler 的 function都必須符合相同的格式。
[補充紀錄]為什麼常看到 EventArgs 會有 object 參數,簡單假設一種情況,
observer 物件可能同時觀察好幾處通知者,並都用同一個 function 去應對,
這時就能夠透過 object 讓 subject 把自己傳過去,這樣就知道是誰傳遞了這個訊息。
由 Delegate 型別 宣告的 Delegate 變數 能夠被託付多個 function,
被託付的 function 會被依序喚起,但這邊不以 Delegate 型別 去宣告變數。
而是以 NotifyEventHandler 型別宣告一個"加了event 關鍵字 的 Delegate 變數 notifyObserver",
通常我們都直接稱他為 event ,但為什麼要加上 event ?
event 本身就是一種 Delegate,能夠接受託付 function ,
但和 Delegate 最大的不同便是它多了一些限制,下面這句話取自 MSDN。
"事件是一種特殊的多點傳送委派,只能從宣告委派的類別或結構內叫用 (發行者類別)。"
白話就是只有宣告 event 變數的類別可以呼叫這個委派,別人都不能呼叫。
因此才會再用 event 關鍵字去宣告變數來加以限制,
不然其實單純使用 Delegate 也能完成這項任務。
註冊事件
到這邊,Boss 類別已經準備好 Event 來讓人註冊了,
讓人註冊的意思就是"把準備好應對的 function 託付給 Boss 的 Event "。
那先來看看 Employee 準備了什麼 function 要託付給 Boss 的 Event 。
兩位 Employee 準備的 function 是相同的,這邊只放其中一位。
Employee 準備了 response() 準備託付給 Boss 的 Event,
特別注意的是框框中的參數部分,必須和 Boss 的 Event 所使用的 Delegate 格式相同。
接著來看如何註冊(託付)。
先把人物準備好之後,透過 += 把想託付的 function 交給 Event 去保管,如下圖框框處。
這邊將 employee 的 response() 託付給 Boss 的 notifyObserver Event,
當 Boss 呼叫 Notify()的時候,便會執行 notifyObserver Event 內被託付的 function,
也就是 employee 的 reponse() ,達到通知的效果。
來看看 Boss 如何實作 Notify()。
呼叫 notifyObserver() 後便會執行 Event 內被委託的所有 funcition。
回顧一下委託給 notifyObserver Event 的 function 順序。
再來看看一下執行結果。
可以發現是照順序 NBA -> Stock 去執行。到這邊已經完成 Observer Pattern 的所有功能了,相較於不使用 Delegate 與 Event 的情況,
Boss 不需要去知道是誰正再觀察它自己,解除了 Subject 對 Observer 的耦合
而 Observer 也不需要遵循 Interface 所定義的名稱去命名要對這個 Event 做出回應的 function。
小結論:
只要 Observer 看的到它想觀察的 Subject 類別 的 Event,就能夠進行觀察、接收通知,
以任意的function進行回應。
假如我不需要來自 Subject 的參數該怎辦?
有時候 Subject 並沒有參數可以傳遞,或者是 Observer 也不需要來自 Subject 的參數,
那麼我們可以把 Delegate 定義為無參數的格式,並用它建立 Event 變數,示範如下:
由於 Delegate 是無參數格式的,那麼要委託給這個 Event 的 function 也必須是無參數的,
所以 Employee 的 response() 也必須修改,如下:
使用者端基本上沒什麼差異,這邊只是把 Notify() 從有參數改為無參數的格式。
Notify() 的實作:
呼叫結果:
EventHandler呢?
(礙於篇幅太長,將於下一篇示範EventHandler)
會想介紹無參數的範例與使用 EventHandler 的範例,
是因為我被這些東西搞混了一段時間,找了很多篇資料才比較清楚這些工具之間的差異,
希望能提供給初學 Event 的人更清楚的比較。

沒有留言:
張貼留言