C#設計模式-觀察者模式
前言
最近開始花點心思研究下設計模式,主要還是讓自己寫的代碼可重用性高、保證代碼可靠性。所謂設計模式,我找了下定義:是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。毫無疑問,設計模式於己於他人於系統都是多贏的;設計模式使代碼編制真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。
為什麼要提倡“Design Pattern(設計模式)”?
根本原因是為了代碼復用,增加可維護性。因此這次我們來學習下設計模式,最後會通過C#語言來實現這些設計模式作為例子,深刻理解其中的精髓。
定義
觀察者模式,有時被稱作發布/訂閱模式,觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
特點
模式中具有的角色
1。 抽象主題(Subject):它把所有觀察者對象的引用保存到一個聚集裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
2。 具體主題(ConcreteSubject):將有關狀態存入具體觀察者對象;在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。
3。抽象觀察者(Observer):為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
4。具體觀察者(ConcreteObserver):實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題狀態協調。
·
優缺點
優點:
一、通知通信
觀察者模式支持廣播通信。被觀察者會向所有的注冊過的觀察者發出通知。
二、聚耦合
觀察者模式在被觀察者和觀察者之間建立了一個抽象的耦合,被觀察者並不知道任何一個具體的觀察者,只是保存著抽象觀察者的列表,每個具體觀察者都符合一個抽象觀察者的接口。
缺點:
一、時間復雜度
如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
二、內聯不足
雖然觀察者模式可以隨時使觀察者知道所觀察的對象發送了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎樣發生變化的。
三、容易出現循環調用
如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰,在使用觀察者模式應特別注意這點。
實現思路
下面以xmfdsh發布一篇博客的例子來說明觀察者模式的實現。關注了xmfdsh的朋友們,便可以通過觀察者模式來實時得到博客進行了更新的信息。當一個抽象模型有兩個方面,其中一個方面依賴於另一個方面,將這兩者封裝在獨立的對象中以使它們可以各自獨立地改變和復用的情況下。從方面的這個詞中可以想到,觀察者模式肯定在AOP(面向方面編程)中有所體現。因此這種需求使用觀察者模式來解決就再恰當不過了。
觀察者向目標“訂閱”它的改變,而目標發生改變後就“通知”所有已經“訂閱”了它的改變的觀察者,從而執行“訂閱”的內容。這種機制的好處在於降低耦合度,分工明確,目標只負責在自身狀態發生改變或做出某種行為時向自身的訂閱清單發出“通知”,而不是直接調用觀察者的行為(方法);觀察者只負責向目標“訂閱”它的變化,以及定義自身在收到目標“通知”後所需要做出的具體行為(也就是訂閱的內容)
復制代碼
// 訂閱號抽象類
public abstract class Blog
{
// 保存訂閱者列表
private List<IObserver> observers = new List<IObserver>();
public string Symbol { get; set; }//描寫訂閱號的相關信息
public string Info { get; set; }//描寫此次update的信息
public Blog(string symbol, string info)
{
this.Symbol = symbol;
this.Info = info;
}
// 對同一個訂閱號,新增和刪除訂閱者的操作
public void AddObserver(IObserver ob)
{
observers.Add(ob);
}
public void RemoveObserver(IObserver ob)
{
observers.Remove(ob);
}
public void Update()
{
// 遍歷訂閱者列表進行通知
foreach (IObserver ob in observers)
{
if (ob != null)
{
ob.Receive(this);
}
}
}
}
// 具體訂閱號類
public class MyBlog : Blog
{
public MyBlog(string symbol, string info)
: base(symbol, info)
{
}
}
// 訂閱者接口
public interface IObserver
{
void Receive(Blog tenxun);
}
// 具體的訂閱者類
public class Subscriber : IObserver
{
public string Name { get; set; }
public Subscriber(string name)
{
this.Name = name;
}
public void Receive(Blog xmfdsh)
{
Console.WriteLine("訂閱者 {0} 觀察到了{1}{2}", Name, xmfdsh.Symbol, xmfdsh.Info);
}
}
// 客戶端測試
class Program
{
static void Main(string[] args)
{
Blog xmfdsh = new MyBlog("xmfdsh", "發布了一篇新博客");
// 添加訂閱者
xmfdsh.AddObserver(new Subscriber("王尼瑪"));
xmfdsh.AddObserver(new Subscriber("唐馬儒"));
xmfdsh.AddObserver(new Subscriber("王蜜桃"));
xmfdsh.AddObserver(new Subscriber("敖尼瑪"));
//更新信息
xmfdsh.Update();
//輸出結果,此時所有的訂閱者都已經得到博客的新消息
Console.ReadLine();
}
}
這個類圖是visual studio生成的,可能看起來比較混亂把,這樣的實現就是觀察者模式的實現。任何時候,只要執行了Update方法,便會自動的去通知推送給訂閱了此訂閱號 的用戶,然而在C#中,我們更多的是使用委托與事件來簡化觀察者模式的實現。
復制代碼
class Program
{
// 委托充當訂閱者接口類
public delegate void NotifyEventHandler(object sender);
// 抽象訂閱號類
public class Blog
{
public NotifyEventHandler NotifyEvent;
public string Symbol { get; set; }//描寫訂閱號的相關信息
public string Info { get; set; }//描寫此次update的信息
public Blog(string symbol, string info)
{
this.Symbol = symbol;
this.Info = info;
}
#region 新增對訂閱號列表的維護操作
public void AddObserver(NotifyEventHandler ob)
{
NotifyEvent += ob;
}
public void RemoveObserver(NotifyEventHandler ob)
{
NotifyEvent -= ob;
}
#endregion
public void Update()
{
if (NotifyEvent != null)
{
NotifyEvent(this);
}
}
}
// 具體訂閱號類
public class MyBlog : Blog
{
public MyBlog(string symbol, string info)
: base(symbol, info)
{
}
}
// 具體訂閱者類
public class Subscriber
{
public string Name { get; set; }
public Subscriber(string name)
{
this.Name = name;
}
public void Receive(Object obj)
{
Blog xmfdsh = obj as Blog;
if (xmfdsh != null)
{
Console.WriteLine("訂閱者 {0} 觀察到了{1}{2}", Name, xmfdsh.Symbol, xmfdsh.Info);
}
}
}
static void Main1(string[] args)
{
Blog xmfdsh = new MyBlog("xmfdsh", "發布了一篇新博客");
Subscriber wnm = new Subscriber("王尼瑪");
Subscriber tmr = new Subscriber("唐馬儒");
Subscriber wmt = new Subscriber("王蜜桃");
Subscriber anm = new Subscriber("敖尼瑪");
// 添加訂閱者
xmfdsh.AddObserver(new NotifyEventHandler(wnm.Receive));
xmfdsh.AddObserver(new NotifyEventHandler(tmr.Receive));
xmfdsh.AddObserver(new NotifyEventHandler(wmt.Receive));
xmfdsh.AddObserver(new NotifyEventHandler(anm.Receive));
xmfdsh.Update();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("移除訂閱者王尼瑪");
xmfdsh.RemoveObserver(new NotifyEventHandler(wnm.Receive));
xmfdsh.Update();
Console.ReadLine();
}
}