觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部觀察者對象,使觀察者對象它們可以自動更新本身。javascript
說白了就是說一個對象,可以經過另一個對象(通知者)的狀態改變自身的狀態。html
問:何時應該用觀察者模式呢?java
答:當一個對象的改變須要同時改變其餘對象的時候。git
觀察者模式所作的工做其實就是在解除耦合。讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另外一邊的變化。設計模式
下面給一個觀察者模式的UML圖:ide
下面給出觀察者模式的代碼結構:post
namespace ConsoleApplication1 { //Subject類,可翻譯爲抽象通知者 abstract class Subject { private IList<Observer> observers = new List<Observer>(); //增長觀察者 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(); } } } //抽象觀察者 abstract class Observer { public abstract void Update(); } //ConcreteSubject類,具體通知者 class ConcreteSubject : Subject { private string subjectState; //具體被觀察者狀態 public string SubjectState { get { return subjectState; } set { subjectState = value; } } } //ConcreteObserver類,具體觀察者 class ConcreteObserver : Observer { private string name; private string observerState; private ConcreteSubject subject; public ConcreteObserver(ConcreteSubject subject, string name) { this.subject = subject; this.name = name; } public override void Update() { observerState = subject.SubjectState; Console.WriteLine("觀察者{0}的新狀態是{1}",name,observerState); } public ConcreteSubject Subject { get { return subject; } set { subject = value; } } } class Program { static void Main(string[] args) { ConcreteSubject s = new ConcreteSubject(); s.Attach(new ConcreteObserver(s, "X")); s.Attach(new ConcreteObserver(s, "Y")); s.Attach(new ConcreteObserver(s, "Z")); s.SubjectState = "ABC"; s.Notify(); Console.ReadKey(); } } }
如今回到《大話設計模式》裏面的例子老闆回來了的例子:學習
namespace ConsoleApplication1 { //通知者接口 interface Subject { void Attach(Observer observer); void Detach(Observer observer); void Notify(); string SubjectState { get; set; } } //老闆通知者,若是老闆沒被發現,那麼老闆進來的,就至關於通知員工老闆回來了 class Boss : Subject { //同事列表 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; } } } //祕書通知者,若是老闆沒被發現,那麼老闆進來的,就至關於通知員工老闆回來了 class Secretary : Subject { //同事列表 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; } } } //抽象觀察者 abstract class Observer { protected string name; protected Subject sub; public Observer(string name, Subject sub) { this.name = name; this.sub = sub; } public abstract void Update(); //模擬對象狀態的改變 } //看股票的同事 class StockObserver : Observer { public StockObserver(string name, Subject sub) : base(name,sub) { } public override void Update() //模擬對象狀態的改變 { Console.WriteLine("{0}{1}關閉股票行情,繼續工做!", sub.SubjectState, name); } } //看NBA的同事 class NBAObserver : Observer { public NBAObserver(string name, Subject sub) : base(name, sub) { } public override void Update() //模擬對象狀態的改變 { Console.WriteLine("{0}{1}關閉NBA直播,繼續工做!", sub.SubjectState, name); } } class Program { static void Main(string[] args) { //老闆 Boss LaoBan = new Boss(); //看股票的同事 StockObserver tongshi1 = new StockObserver("魏關宅",LaoBan); //看NBA的同事 NBAObserver tongshi2 = new NBAObserver("易觀察", LaoBan); LaoBan.Attach(tongshi1); LaoBan.Attach(tongshi2); LaoBan.Detach(tongshi1); //老闆回來了 LaoBan.SubjectState = "我老闆回來了"; //調用通知方法 發出通知 LaoBan.Notify(); //祕書 Secretary secretary = new Secretary(); //看股票的同事 StockObserver tongshi3 = new StockObserver("魏關宅", secretary); //看NBA的同事 NBAObserver tongshi4 = new NBAObserver("易觀察", secretary); secretary.Attach(tongshi3); secretary.Attach(tongshi4); //老闆回來了 secretary.SubjectState = "我是祕書,老闆回來了"; //調用方法,發出通知 secretary.Notify(); Console.ReadKey(); } } }
其實事件委託也可以實現觀察者模式,並且還好,例如當一個觀察者代碼已經寫好了以後,用委託能夠再不用改動已經寫好具體的觀察者代碼就實現觀察者模式。如下給出用委託實現的觀察者模式代碼:this
namespace ConsoleApplication1 { //通知者接口 interface Subject { void Notify(); string SubjectState { get; set; } } //聲明一個類外委託 delegate void EventHandler(); //老闆通知者,若是老闆沒被發現,那麼老闆進來的,就至關於通知員工老闆回來了 class Boss : Subject { //聲明一事件Update,類型爲委託EventHandler public event EventHandler Update; private string action; //通知 public void Notify() { Update(); //在訪問通知方法時,調用"更新" } //老闆狀態 public string SubjectState { get { return action; } set { action = value; } } } //祕書通知者,若是老闆沒被發現,那麼老闆進來的,就至關於通知員工老闆回來了 class Secretary : Subject { //聲明一事件Update,類型爲委託EventHandler public event EventHandler Update; private string action; //通知 public void Notify() { Update(); } //老闆狀態 public string SubjectState { get { return action; } set { action = value; } } } //看股票的同事 class StockObserver { private string name; private Subject sub; public StockObserver(string name, Subject sub) { this.name = name; this.sub = sub; } public void CloseStock() //模擬對象狀態的改變 { Console.WriteLine("{0}{1}關閉股票行情,繼續工做!", sub.SubjectState, name); } } //看NBA的同事 class NBAObserver { private string name; private Subject sub; public NBAObserver(string name, Subject sub) { this.name = name; this.sub = sub; } public void CloseNBA() //模擬對象狀態的改變 { Console.WriteLine("{0}{1}關閉NBA直播,繼續工做!", sub.SubjectState, name); } } class Program { static void Main(string[] args) { Boss LaoBan = new Boss(); //老闆 StockObserver tongshi1 = new StockObserver("魏關宅", LaoBan); //看股票的同事 NBAObserver tongshi2 = new NBAObserver("易觀察", LaoBan); //看NBA的同事 LaoBan.SubjectState = "我老闆回來了"; //老闆回來了 LaoBan.Update += new EventHandler(tongshi1.CloseStock); //將關閉股票的方法掛鉤到老闆的Update LaoBan.Update += new EventHandler(tongshi2.CloseNBA); LaoBan.Notify(); //調用通知方法 發出通知 Console.ReadKey(); } } }
輸出結果如圖所示:spa