觀察者模式
觀察者模式:定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主體對象,這個主題對象在狀態發生變化時,會通知所有觀察者。當一個對象改變需要同時改變其他對象,而且他不知道具體有多少對象需要改變的時候,應該考慮使用觀察者模式。
觀察者結構圖:
使用情境:老闆回來通知員工需要進入工作狀態。
定義觀察者的抽象類別: 複製代碼 代碼如下:abstract class Observer
{
protected string name;
protected ISubject sub;
public Observer(string name, ISubject sub)
{
this.name = name;
this.sub = sub;
}
public abstract void Update();
}
看NBA直播的同事: 複製代碼 代碼如下://使用OO
class NBAObserver : Observer
{
public NBAObserver(string name, ISubject sub)
: base(name, sub)
{ }
public override void Update()
{
//throw new NotImplementedException();
Console.WriteLine("{0} {1}關閉NBA直播,繼續工作!",sub.SubjectState,name);
}
}
//使用事件和委託
class NBAObserver2
{
private string name;
private ISubject2 sub;
public NBAObserver2(string name, ISubject2 sub)
{
this.name = name;
this.sub = sub;
}
public void CloseStockMarket()
{
Console.WriteLine("{0} {1}關閉NBA直播,繼續工作!", sub.SubjectState, name);
}
}
看股票的同事: 複製代碼 代碼如下://使用OO
class StockObserver : Observer
{
public StockObserver(string name, ISubject sub) : base(name,sub)
{ }
public override void Update()
{
//throw new NotImplementedException();
Console.WriteLine("{0} {1}關閉股票行情,繼續工作!",sub.SubjectState,name);
}
}
//使用事件和委託
class StockObserver2
{
private string name;
private ISubject2 sub;
public StockObserver2(string name, ISubject2 sub)
{
this.name = name;
this.sub = sub;
}
public void CloseNBA()
{
Console.WriteLine("{0} {1}關閉股票行情,繼續工作!", sub.SubjectState, name);
}
}
上的身份是訂閱者,下面定義發行者: 複製代碼 代碼如下://使用OO
interface ISubject
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjectState
{
get;
set;
}
}
class Boss : ISubject
{
private IList<Observer> observers = new List<Observer>();
private string action;
public void Attach(Observer observer)
{
observers.Add(observer);
}
public void Detach(Observer observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (Observer o in observers)
{
o.Update();
}
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
//使用事件和委託
interface ISubject2
{
void Notify();
string SubjectState
{
get;
set;
}
}
delegate void EventHandler();
class Boss2 : ISubject2
{
public event EventHandler Update;
private string action;
public void Notify()
{
Update();
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
主函數調用: 複製代碼 代碼如下:class Program
{
static void Main(string[] args)
{
//觀察者模式OO實現
Boss huhansan = new Boss();
StockObserver tongshi1 = new StockObserver("name1",huhansan);
NBAObserver tonshi2 = new NBAObserver("name2", huhansan);
huhansan.Attach(tongshi1);
huhansan.Attach(tonshi2);
huhansan.SubjectState = "我1回來了";
huhansan.Notify();
//觀察者模式c#事件實現
Boss2 huhansan2 = new Boss2();
StockObserver2 tongshi3 = new StockObserver2("name3", huhansan2);
NBAObserver2 tonshi4 = new NBAObserver2("name4", huhansan2);
huhansan2.Update += new EventHandler(tongshi3.CloseNBA);
huhansan2.Update += new EventHandler(tonshi4.CloseStockMarket);
huhansan2.SubjectState = "我2回來了";
huhansan2.Notify();
Console.ReadKey();
}
}
委託就是一種引用方法的類型,一旦為委託分配了方法,委託將與該方法具有完全相同的行為。委託方法的使用可以像其他任何方法一樣,具有參數和傳回值。委託可以看做是對函數的抽象,是函數的一個類,委託執行個體代表一個具體的函數,而且一個委託可以搭載多個方法,所有方法被依次喚醒。
1 觀察者模式
一個簡單的例子,比如說貓叫,老鼠跑,主人被驚醒。
在不知道觀察者模式之前,我們的代碼可能是這樣的。複製代碼 代碼如下://老鼠類
class Mouse
{
public void Run()
{
Console.WriteLine("老鼠跑了!");
}
}
//主人類
class Master
{
public void Wake()
{
Console.WriteLine("主人醒了!");
}
}
//貓類
class Cat
{
public void Cry ()
{
Console.WriteLine("貓叫了!");
new Mouse().Run();//貓叫的同時,調用老鼠跑的方法。
new Master().Wake();//貓叫的同時,調用主人醒的方法。
}
}
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Cry();
Console.ReadLine();
}
}
這個代碼基本上能實現所有的功能。但是,這個方法特別不利用擴充,
試想,如果,貓叫後,狗也叫,那是不是也要在貓叫的方法裡重新加入狗叫的方法?
或者說,貓叫後,主人他老婆也醒了,是不是也要在貓叫的方法裡加入他老婆醒的方法呢?
顯然,這樣的代碼不利用維護,也不是物件導向的代碼。
觀察者模式能夠很好的解決這個問題。
觀察者模式定義對象間的一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於他的對象都得到通知並自動更新。在我們的例子中,貓和老鼠,主人,狗,主人他老婆是一對多的依賴,當貓叫時,所有依賴於它的對象都會自動執行某個操作。
觀察者模式的實現,一般有下面四部分組成。
1.ISubject介面(抽象目標),含方法Notify,Register,UnRegister(名字可以自己任意取名)
2.Subject類(實體目標),實現ISubject介面,一般只有一個
3.IObservable介面(抽象觀察者)。
4 Observer類(實體觀察者),實現IObservable介面,一般有多個。
觀察者模式中的“註冊--通知--登出”圖示:
1. 觀察者(Observer)將自己(Regiester)註冊到被觀察對象(Subject)中,被觀察對象將觀察者放在一個容器(Container)。Container一般為Ilist,Arraylist等資料結構,存放多個IObservable介面變數。
2.當被觀察對象(Subject)發生變化(中的AskPriceChanged)時,容器(Container)中的所有觀察者(Observer)都得到通知(Notify 方法),此時觀察者會自動執行某些方法。
3.當觀察者不想繼續觀察被觀察者時,可以登出(UnRegiester方法)
上面的例子中改造後變成:
1.ISubject介面:複製代碼 代碼如下:interface ISubject
{
void Notify();//主題變動時,通知雖有觀察者
void Regiester(IObservable o);//觀察者註冊
void UnRegiester(IObservable o);//觀察者取消註冊,此時主題發生任何變動,觀察者都不會得到通知。
}
2.Subject 類:複製代碼 代碼如下:class Cat : ISubject
{
private IList<IObservable> observers = new List<IObservable>();
public void Notify()
{
foreach (IObservable o in observers) //逐個通知觀察者
{
o.Action();
}
}
public void Regiester(IObservable o)
{
if (o != null || !observers.Contains(o))
{
observers.Add(o);
}
}
public void UnRegiester(IObservable o)
{
if (observers != null && observers.Contains(o))
{
observers.Remove(o);
}
}
public void Cry()
{
Console.WriteLine("貓叫了!");
Notify();
}
}
3. IObservable 介面:複製代碼 代碼如下:interface IObservable
{
void Action();//觀察者對主題變動所對應的操作
}
4.Observer類(2個,Mouse和Master)複製代碼 代碼如下:class Mouse : IObservable
{
public void Action()
{
Console.WriteLine("鼠跑了!");
}
}
class Master : IObservable
{
public void Action()
{
Console.WriteLine("主人醒了!");
}
}
5.主程式複製代碼 代碼如下:Mouse mouse = new Mouse();
Master master = new Master();
Cat cat = new Cat();
cat.Regiester(mouse);
cat.Regiester(master);
cat.Cry();
Console.ReadLine();
這樣就實現了觀察者模式,通過把依賴類註冊到主體類中,當主體類發生變化時,所有依賴類都得到了通知。如果需要擴充,比如說象上面例子的狗也叫了,我們可以定義一個狗類,然後在主程式中把狗對象註冊到貓類中就可以了。如果不想依賴於貓類,也可以通過UnRegiester方法取消綁定。
同時,這也符合設計中的高內聚,低耦合的原則。
。複製代碼 代碼如下:using System;
using System.Collections.Generic;
using System.Text;
namespace Sample
{
public delegate void CatCallEventHandler();
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
Mouse mouse = new Mouse(cat);
Master master = new Master(mouse);
cat.Call();
}
}
class Cat
{
public event CatCallEventHandler catevent;
public void Call()
{
Console.WriteLine("喵喵.....");
catevent();
}
}
class Mouse
{
public event CatCallEventHandler mouseevent;
public Mouse(Cat cat)
{
cat.catevent += new CatCallEventHandler(this.MouseRun);
}
public void MouseRun()
{
Console.WriteLine("老鼠跑");
mouseevent();
}
}
class Master
{
public Master(Mouse mouse)
{
mouse.mouseevent+=new CatCallEventHandler(this.JingXing);
}
public void JingXing()
{
Console.WriteLine("主人被驚醒");
}
}
}