前往
大廳
主題

[Design Pattern C#] Observer Pattern 觀察者模式 Event &Delegate

帥氣跳蚤蛋 | 2021-11-30 20:35:48 | 巴幣 1106 | 人氣 662

觀察者模式的主要功能是當,若被觀察的物件發生狀態的變化,會主動通知所有的觀察者.

舉個例子:像是巴哈姆特、Dcard這些討論區,
會提供會員帳號訂閱與追蹤特定主題或文章的功能,而當該主題或文章有新的討論時,則會發布通知訊息給所有訂閱的帳號.

在觀察者模式中,
會員帳號就是扮演觀察者的角色,而訂閱的文章或主題就是被觀察者,
當訂閱的主題或文章有新的討論,則表示物件發生了狀態變化,
於是發布通知訊息,告知訂閱的帳號已有新的主題或討論.

=============================================================================
巴哈小屋的程式碼不易排版,希望有好的閱讀體驗可至,網頁好圖版,Medium部落格: 跳蚤的蛋蛋跳蚤蛋
=============================================================================

C#中有提供Event、Delegate與EventHandler委派,可快速的實踐觀察者模式,
由Forum作為被觀察者,GoldMembership與DiamondMembership為觀察者,
由觀察者向被觀察者訂閱,
當被觀察者發出通知,所有訂閱的觀察者即可收到事件,
下圖即是觀察者模式想呈現的結果:
Article article = new Article() { ArticleTitle = "帥氣跳蚤蛋已在開發代號「帥哥」的遊戲新作,傳言前作帥到爆表的主角將會回歸!?" }; //要發送的資料
Forum forum = new Forum(); //被觀察者
GoldMembership goldMembership = new GoldMembership(); //觀察者
DiamondMembership diamondMembership = new DiamondMembership(); //觀察者

forum.ForumNotified += goldMembership.OnForumNotified; //訂閱事件
forum.ForumNotified += diamondMembership.OnForumNotified; //訂閱事件
forum.Notified(article); //發送通知

在本帥的GitHub有提供本文完整的程式原始碼:

建立Article類別,被觀察者要發送的資料,
擁有最新的文章標題資訊.
public class Article
{
public string ArticleTitle { set; get; }
}

再來建立通知的事件,
ForumEventArgs類別需繼承自EventArgs,
因event須包含一個EventArgs型別或其衍生型別的參數,
被觀察者發出通知時會發出此事件,
當通知後,觀察者即會收到此事件,含有被觀察者的所有資訊.
public class ForumEventArgs:EventArgs
{
public Article Article { get; set; }
}

接著來建立被觀察者,
第3行的ForumNotifyEventHandler: 使用delegate進行事件型態宣告,無返回參數並包含2個參數:事件來源的物件、觸發的事件.
第4行的ForumNotified: 建立ForumNotifyEventHandler型態的event.
在此類別中有包含2個方法:Notified與OnForumNotified
第5行的Notified: 發出通知,並由OnForumNotified來觸發ForumNotified事件.
第11行的OnForumNotified: 對所有訂閱者發出通知,帶有目前最新文章的資訊,觸發的事件要標記為virtual.
public class Forum
{
public delegate void ForumNotifyEventHandler(object source, ForumEventArgs args);
public event ForumNotifyEventHandler ForumNotified;
public void Notified(Article forum)
{
Console.WriteLine("準備發送通知.");
OnForumNotified(forum);
}

public virtual void OnForumNotified(Article forum)
{
if (forum != null)
ForumNotified(this, new ForumEventArgs() { Article= forum });
}

}

在程式的第3行,事件的宣告使用EventHandler來簡化程式,
取代上面程式第3、4的delegate與event,
而第12行的Invoke,與上面程式的13、14行為相同功能.
public class Forum
{
public EventHandler<ForumEventArgs> ForumNotified;
public void Notified(Article forum)
{
Console.WriteLine("準備發送通知.");
OnForumNotified(forum);
}

public virtual void OnForumNotified(Article forum)
{
ForumNotified?.Invoke(this, new ForumEventArgs() { Article = forum });
}

}

最後開始實作觀察者,
我們實作GoldMembership與DiamondMembership兩種觀察者,
觀察者需建立事件處理方法,此方法需與被觀察者的EventHandler型態一致,
將此方法註冊給被觀察者,當被觀察者發出通知,即會觸發此方法.
public class GoldMembership
{
public void OnForumNotified(object source, ForumEventArgs args)
{
Console.WriteLine($"黃金會員您好! 討論區有新的文章:{args.Article.ArticleTitle} ,只需支付少許費用即可觀看最新內容!");
}
}

public class DiamondMembership
{
public void OnForumNotified(object source, ForumEventArgs args)
{
Console.WriteLine($"鑽石會員您好! 討論區有新的文章:{args.Article.ArticleTitle} ,您可免費瀏覽所有最新內容!");
}
}

現在回到Main,
第6、7行GoldMembership與DiamondMembership兩個觀察者,使用OnForumNotified,對被觀察者Forum進行訂閱.
在第10行forum觸發事件通知,使兩個訂閱者執行OnForumNotified方法.
Article article = new Article() { ArticleTitle = "帥氣跳蚤蛋已在開發代號「帥哥」的遊戲新作,傳言前作帥到爆表的主角將會回歸!?" }; //要發送的資料
Forum forum = new Forum(); //被觀察者
GoldMembership goldMembership = new GoldMembership(); //觀察者
DiamondMembership diamondMembership = new DiamondMembership(); //觀察者

forum.ForumNotified += goldMembership.OnForumNotified; //訂閱事件
forum.ForumNotified += diamondMembership.OnForumNotified; //訂閱事件
forum.Notified(article); //發送通知

/* Result:
* 準備發送通知.
* 黃金會員您好! 討論區有新的文章:帥氣跳蚤蛋已在開發代號「帥哥」的遊戲新作,傳言前作帥到爆表的主角將會回歸!? ,只需支付少許費用即可觀看最新內容!
* 鑽石會員您好! 討論區有新的文章:帥氣跳蚤蛋已在開發代號「帥哥」的遊戲新作,傳言前作帥到爆表的主角將會回歸!? ,您可免費瀏覽所有最新內容!
*/

程式的輸出結果:

關於事件更詳細的說明可參考官方頁面: 處理和引發事件
送禮物贊助創作者 !
0
留言

創作回應

樂小呈
OOP 的神之模式
2021-12-01 10:24:46
帥氣跳蚤蛋
這個模式真滴好用~[e19]
2021-12-01 19:06:43

更多創作