本篇文章將簡單介紹觀察者模式 (Observer Pattern) 的概念,並包含 C# 範例程式碼。
摘要
- 觀察者模式將資料的接收拆解,分為發佈者和訂閱者兩個角色。可以有多個訂閱者向由一個發佈者訂閱訊息,發佈者可以發送事件通知訂閱者,訂閱者可以隨時訂閱或取消訂閱事件通知。
- 透過撰寫兩個 Interface 建立觀察者模式,分別是發佈者 Interface ,裡面可以包含訂閱者的清單 (List) ,另一個是訂閱者 Interface ,發佈者裡面要有以下方法:
- 訂閱的方法 (Subscribe)
- 取消訂閱的方法 (Unsubscribe)
- 通知的方法 (Notify)
以讓訂閱者進行訂閱。
- C# 的 Delegate、Event Handler 機制可以實踐觀察者模式。
範例
以下是觀察者模式的基本實作,包含訂閱、取消訂閱、通知等方法。
public class ObserverPattern
{
///<summary> 發佈者 (主題) 類別 </summary>
public class Subject{
List<Observer> observers = new List<Observer>();
public void Subscribe(Observer ob)
{
observers.Add(ob);
}
public void Unsubscribe(Observer ob)
{
observers.Remove(ob);
}
public void Notify()
{
foreach(Observer ob in observers){
ob.GotNotification();
}
}
}
///<summary> 訂閱者 (觀察) 類別 </summary>
public class Observer{
int id;
public Observer(int i)
{
id = i;
}
public void GotNotification()
{
Console.WriteLine(id + " got notification from subject.");
}
}
///<summary> 主程式 </summary>
public void Run()
{
Observer a = new Observer(1);
Observer b = new Observer(2);
Observer c = new Observer(3);
Subject s = new Subject();
s.Subscribe(a);
s.Subscribe(b);
s.Subscribe(c);
s.Notify();
s.Unsubscribe(b);
s.Notify();
}
}
但是沒有實作 Interface 的話,假如同時有多種主題要發佈,也有不同的觀察者行為要處理,還是要加入 Interface,以減少類別間的耦合。
public class ObserverWithInterface
{
///<summary> 發佈者 (主題) 介面 </summary>
public interface Subject{
public void Subscribe(Observer ob);
public void Unsubscribe(Observer ob);
public void Notify();
}
///<summary> 訂閱者 (觀察) 介面 </summary>
public interface Observer{
public void GotNotification(string message);
}
///<summary> 音樂發佈者 </summary>
public class MusicSubject: Subject{
List<Observer> observers = new List<Observer>();
public void Subscribe(Observer ob)
{
observers.Add(ob);
}
public void Unsubscribe(Observer ob)
{
observers.Remove(ob);
}
public void Notify()
{
foreach(Observer ob in observers){
ob.GotNotification("Pop Music started!");
}
}
}
///<summary> 卡通發佈者 </summary>
public class CartoonSubject: Subject{
List<Observer> observers = new List<Observer>();
public void Subscribe(Observer ob)
{
observers.Add(ob);
}
public void Unsubscribe(Observer ob)
{
observers.Remove(ob);
}
public void Notify()
{
foreach(Observer ob in observers){
ob.GotNotification("Now Playing: Doraemon");
}
}
}
///<summary> 兒童訂閱者 </summary>
public class KidsObserver:Observer{
int id;
public KidsObserver(int i)
{
id = i;
}
public void GotNotification(string message)
{
Console.WriteLine("Kid: " + id + " got notification \"" + message + "\" from subject.");
}
}
///<summary> 青年訂閱者 </summary>
public class YoungObserver:Observer{
int id;
public YoungObserver(int i)
{
id = i;
}
public void GotNotification(string message)
{
Console.WriteLine("Young " + id + " got notification \"" + message + "\" from subject.");
}
}
///<summary> 主程式 </summary>
public void Run()
{
Observer kids1 = new KidsObserver(1);
Observer young2 = new YoungObserver(2);
Observer kids3 = new KidsObserver(3);
Subject cartoon = new CartoonSubject();
Subject music = new MusicSubject();
cartoon.Subscribe(kids1);
cartoon.Subscribe(kids3);
music.Subscribe(young2);
music.Subscribe(kids3);
cartoon.Notify();
music.Notify();
}
}
參考資料
- Design Pattern - 只要你想知道,我就告訴你 - 觀察者模式( Observer Pattern ) feat. TypeScript - by 神Q超人 - Enjoy life enjoy coding - Medium
- 觀念 設計模式–Observer Pattern - 我,傑夫。開發人
- [C#]Observer Pattern到Delegate和Event - 全端開發人員天梯 - 點部落
- [Design Pattern] 觀察者模式 Observer Pattern - Jesper程式學習筆記 - 點部落: 如果既有類別也想實作觀察者模式,可以使用 Delegate + EventHandler 發送通知。
- Observer Pattern abstract vs interface - Stack Overflow: 要用 Abstract Class 預先寫好訂閱行為,或是用 Interface 自定義訂閱行為?都可以,取決於使用情境。