先看下觀察者模式的定義:
? ? ? The Observer Pattern defines a one-to-many denpendency between objects so that when one object changes state, all of its dependents are notified and updated automatically.:觀察者模式定義了對象間一對多依賴關系,使得當一個對象改變狀態,則所有依賴于它的對象都會得到通知并被自動更新。
? ? ? 觀察者模式又叫發布-訂閱(Publish--Subscribe)模式,模型—視圖(Model/View)模式,源—監聽器(Source/Listener)模式等。
? ? ? 觀察者模式定義了一種一對多的關系,讓多個觀察者同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使他們能夠自動能夠更新自己。
? ? ? 實現觀察者模式比較直觀的一種是“注冊-----通知----撤銷注冊”的形式。
首先,觀察者(Observer)將自己注冊到主題對象(Subject)中,主題對象將觀察者存放在一個容器(Container)中.
其次,主題對象發生了某種變化,從容器中得到所有注冊過的觀察者,將變化通知觀察者。
最后,觀察者告訴主題對象要撤銷觀察,主題對象從容器中將觀察者去除。
? ? ? 觀察者將自己注冊到主題對象的容器中時,主題對象不應該過問觀察者的具體類型,而是使用觀察者的接口。這樣做的優點是:假定程序中還有別的觀察者,那么只要這個觀察者也是相同的接口實現即可。一個主題對象可以對應多個觀察者,當主題對象發生變化的時候,他可以將消息一一通知給所有的觀察者。基于接口,而不是具體的實現------這一點為程序提供了更大的靈活性。
? ? ? 舉個例子:貓叫了一聲,驚醒了主人,嚇跑了老鼠。這個例子就可以用觀察者模式來實現。貓是主題對象,主人和老鼠是觀察者。具體實現類圖如下:
C#代碼實現:
public interface IObserver
? ? {
? ? ? ? void Update();
? ? }
? ? public interface ISubject
? ? {
? ? ? ? void AddObserver(IObserver obs);
? ? ? ? void RemoveObserver(IObserver obs);
? ? ? ? void Notify();
? ? }
? ? public class Mouse : IObserver
? ? {
? ? ? ? public void Update()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("Mouse is escaped");
? ? ? ? }
? ? }
? ? public class Master : IObserver
? ? {
? ? ? ? public void Update()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("Master is waken");
? ? ? ? }
? ? }
? ? public class Cat : ISubject
? ? {
? ? ? ? List<IObserver> list = new List<IObserver>();
? ? ? ? public void AddObserver(IObserver obs)
? ? ? ? {
? ? ? ? ? ? list.Add(obs);
? ? ? ? }
? ? ? ? public void RemoveObserver(IObserver obs)
? ? ? ? {
? ? ? ? ? ? list.Remove(obs);
? ? ? ? }
? ? ? ? public void Notify()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("Cat is crying");
? ? ? ? ? ? foreach (IObserver obs in list)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? obs.Update();
? ? ? ? ? ? }
? ? ? ? }
}
static void Main(string[] args)
{
? ? IObserver mouse = new Mouse();
? ? IObserver master = new Master();
? ? ISubject cat = new Cat();
? ? cat.AddObserver(mouse);
? ? cat.AddObserver(master);
? ? cat.Notify();
}
? ? ? 在.NET中,利用事件和委托來實現Observer模式更為簡單,也是一中更好的解決方案。在C#的事件中,委托充當了抽象的Observer接口,而提供事件的對象充當了目標(Subject)對象。其實委托是比抽象Observer接口更為松耦合的設計,因為委托只要求掛接的方法的聲明部分必須符合委托聲明的格式,而不需要像接口一樣必須要求類去完全實現之。觀察者模式之所以叫觀察者模式,并不是因為內部使用了ISubject,IObserver等來實現,而是因為這個模式解決了如下問題:“觀察者模式定義了對象間的一種一對多依賴關系,使得每當一個對象改變狀態,則所以依賴于它的對象都會得到通知并被自動更新”。也就是說,所有解決這個問題的方法都可以稱作觀察者模式。而且接口的概念也絕對不局限于C#里面的interface,接口只是一個契約,用來規范代碼的行為,delegate也是一個接口,它規定了什么樣的方法可以加載到delegate對應的event中,這也是一個契約,只是這個契約要比interface更簡單。
下面我們用委托事件機制來實現上面的功能,代碼如下:
public class Cat
? ? ? ? {
? ? ? ? ? ? public delegate void NotifyEventHandler();
? ? ? ? ? ? public event NotifyEventHandler NotifyEvent;
? ? ? ? ? ? public void Notify()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (NotifyEvent != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("Cat is crying");
? ? ? ? ? ? ? ? ? ? NotifyEvent();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? public class Master
? ? ? ? {
? ? ? ? ? ? public void Update()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("Master is waken");
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? public class Mouse
? ? ? ? {
? ? ? ? ? ? public void Update()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Console.WriteLine("Mouse is escaping");
? ? ? ? ? ? }
? ? ? ? }
static void Main(string[] args)
{
? Cat cat = new Cat();
? Master master = new Master();
? Mouse mouse = new Mouse();
? Cat.NotifyEventHandler notifyMaster = new Cat.NotifyEventHandler(master.Update);
Cat.NotifyEventHandler notifyMouse = new Cat.NotifyEventHandler(mouse.Update);
? cat.NotifyEvent += notifyMaster;
? cat.NotifyEvent += notifyMouse;
? cat.Notify();
}
應用場景:
對一個對象狀態的更新,需要其他對象同步更新,而且其他對象的數量動態可以變化。
對象僅需要將自己的更新通知給其他對象,而不需要知道其他對象的細節。
優點:
Subject 和obserer之間是送耦合的,分別可以各自獨立改變。
Subject在發送廣播通知的時候,無須指定具體的Observer,Observer可以自己決定是否需要訂閱Subject的通知。
遵守大部分GRASP原則和常用設計原則,高內聚,低耦合。
缺點:
如果一個Subject被大量的Observer訂閱的話,在廣播通知的時候可能會有效率問題。
相關原則
我們知道設計原則遠比模式重要,學習設計模式的同時一定要注意體會設計原則的理念。現在我們看看觀察者模式都符合的設計原則。
Identify the aspects of your application that vary and separate them from what stays the same(找到系統中變化的部分,將變化的部分同其他穩定的部分隔開). 在觀察者模式的應用場景里面變化的部分是Subject的狀態和Observer的數量。使用Observer模式可以很好的將這兩部分隔離開,我們可以任意改變Observer的數量而不需要去修改Subject, 而Subject的狀態也可以任意修改,同樣不會對其Observer有任何影響。
Program to an interface ,not an implementation(面向接口編程,而不要面向實現編程)? Subject和Observer都使用接口來實現。Subject只需要跟蹤那些實現了IObserver接口的對象,所以其只依賴于IObserver;而所有Observer都通過ISubject接口來注冊,撤銷,接收通知,所有它們也只依賴于ISubject;所以是面向接口編程的,這樣的實現方式使得Subject和Observer之間完全沒有任何的耦合。
Favor composition over inheritance(優先使用對象組合,而非類繼承) 觀察者模式使用對象組合將Subject和若干Observer聯系起來。它們之間的關系不是通過類的繼承而是在運行時的動態組合。
原文轉載出處: