一、概述
在軟件設計工作中會存在對象之間的依賴關系,當某一對象發生變化時,所有依賴它的對象都需要得到通知。如果設計的不好,很容易造成對象之間的耦合度太高,難以應對變化。使用觀察者模式可以降低對象之間的依賴,以松耦合的方式實現這一目標。
二、觀察者模式
觀察者模式定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並自動更新。其結構圖如下:

Subject知道它的所有觀察者並提供了觀察者注冊和刪除訂閱的接口。
Observer為那些在目標發生改變時需獲得通知的對象定義一個更新接口。
ConcreteSubject實現Subject接口,當改變狀態時向依賴於它的ConcreteObserver發送通知。
ConcreteObserver實現Observer的更新接口,使得自身能根據ConcreteSubject狀態的不同而做出相應的改變。
觀察者模式分為推模式和拉模式兩種。推模式是當有通知時,把依賴對象的信息以參數的形式傳遞給所有觀察者,而拉模式通知方法本身並不帶任何的參數,是由觀察者自己到依賴對象那里取回相關信息。在推模式下,所有觀察者都通過參數傳遞的方式得到依賴對象的全部信息,與依賴對象之間的耦合較低,但不能實現“按需所取”所需要信息的。而拉模式僅僅是通知觀察者,至於要不要提取依賴對象的信息則是觀察者自己的事情,這么一來就實現“按需所取”,但往往要在ConcreteObserver里保存一個ConcreteSubject的引用,與ConcreteSubject的耦合也加強了。
觀察者模式的Subject一般需要提供觀察者注冊和刪除訂閱的接口,但在.NET中,往往可以利用事件和委托的特性來實現觀察者模式,這是一種更為優雅的方案。
三、示例
我們現在利用事件實現觀察者模式。我們設計一個信用卡消費的簡單例子,在消費的同時需要對用戶賬戶進行扣款,同時對用戶進行短信提醒。
首先定義信用卡類,當消費金額變動時會觸發Notify方法通知該對象的所有觀察者。
View Code
1 public class CreditCard : EventArgs 2 { 3 private float _spendAmount; 4 public event EventHandler<CreditCard> SpendMoney; 5 6 public float SpendAmount 7 { 8 get 9 { 10 return _spendAmount; 11 } 12 set 13 { 14 _spendAmount = value; 15 Notify(); 16 } 17 } 18 19 private void Notify() 20 { 21 if (SpendMoney != null) 22 { 23 SpendMoney(this, this); 24 } 25 } 26 }
接着定義Observer接口,並使用戶帳戶類和短信提醒類實現這個接口,其中這兩個ConcreteObserver類的Update方法簽名必須與CreditCard中的事件SendMoney一致,否則就無法注冊到CreditCard。
View Code
1 public interface IObserver<T> 2 { 3 void Update(Object sender, T e); 4 } 5 6 public class SMSNotify : IObserver<CreditCard> 7 { 8 public void Update(Object sender, CreditCard e) 9 { 10 Console.WriteLine("Sms notify.Spend {0}", e.SpendAmount); 11 } 12 } 13 14 public class Account : IObserver<CreditCard> 15 { 16 private float _accountAmount; 17 18 public Account(float accountAmount) 19 { 20 _accountAmount = accountAmount; 21 } 22 23 public void Update(Object sender, CreditCard e) 24 { 25 _accountAmount += e.SpendAmount; 26 Console.WriteLine("Account amount is {0}", _accountAmount); 27 } 28 }
最后看一下客戶端調用。
View Code
1 static void Main(string[] args) 2 { 3 CreditCard creditCard = new CreditCard(); 4 SMSNotify sms = new SMSNotify(); 5 Account account = new Account(1000); 6 creditCard.SpendMoney += account.Update; 7 creditCard.SpendMoney += sms.Update; 8 creditCard.SpendAmount = 200; 9 }
