1、概述
在軟件設計工做中會存在對象之間的依賴關係,當某一對象發生變化時,全部依賴它的對象都須要獲得通知。若是設計的很差,很容易形成對象之間的耦合度過高,難以應對變化。使用觀察者模式能夠下降對象之間的依賴,以鬆耦合的方式實現這一目標。this
2、觀察者模式
觀察者模式定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並自動更新。其結構圖以下:spa
Subject知道它的全部觀察者並提供了觀察者註冊和刪除訂閱的接口。
Observer爲那些在目標發生改變時需得到通知的對象定義一個更新接口。
ConcreteSubject實現Subject接口,當改變狀態時向依賴於它的ConcreteObserver發送通知。
ConcreteObserver實現Observer的更新接口,使得自身能根據ConcreteSubject狀態的不一樣而作出相應的改變。
觀察者模式分爲推模式和拉模式兩種。推模式是當有通知時,把依賴對象的信息以參數的形式傳遞給全部觀察者,而拉模式通知方法自己並不帶任何的參數,是由觀察者本身到依賴對象那裏取回相關信息。在推模式下,全部觀察者都經過參數傳遞的方式獲得依賴對象的所有信息,與依賴對象之間的耦合較低,但不能實現「按需所取」所須要信息的。而拉模式僅僅是通知觀察者,至於要不要提取依賴對象的信息則是觀察者本身的事情,這麼一來就實現「按需所取」,但每每要在ConcreteObserver裏保存一個ConcreteSubject的引用,與ConcreteSubject的耦合也增強了。
觀察者模式的Subject通常須要提供觀察者註冊和刪除訂閱的接口,但在.NET中,每每能夠利用事件和委託的特性來實現觀察者模式,這是一種更爲優雅的方案。設計
3、示例
咱們如今利用事件實現觀察者模式。咱們設計一個信用卡消費的簡單例子,在消費的同時須要對用戶帳戶進行扣款,同時對用戶進行短信提醒。
首先定義信用卡類,當消費金額變更時會觸發Notify方法通知該對象的全部觀察者。code
public class CreditCard : EventArgs { private float _spendAmount; public event EventHandler<CreditCard> SpendMoney; public float SpendAmount { get { return _spendAmount; } set { _spendAmount = value; Notify(); } } private void Notify() { if (SpendMoney != null) { SpendMoney(this, this); } } }
接着定義Observer接口,並使用戶賬戶類和短信提醒類實現這個接口,其中這兩個ConcreteObserver類的Update方法簽名必須與CreditCard中的事件SendMoney一致,不然就沒法註冊到CreditCard。server
public interface IObserver<T> { void Update(Object sender, T e); } public class SMSNotify : IObserver<CreditCard> { public void Update(Object sender, CreditCard e) { Console.WriteLine("Sms notify.Spend {0}", e.SpendAmount); } } public class Account : IObserver<CreditCard> { private float _accountAmount; public Account(float accountAmount) { _accountAmount = accountAmount; } public void Update(Object sender, CreditCard e) { _accountAmount += e.SpendAmount; Console.WriteLine("Account amount is {0}", _accountAmount); } }
最後看一下客戶端調用。對象
static void Main(string[] args) { CreditCard creditCard = new CreditCard(); SMSNotify sms = new SMSNotify(); Account account = new Account(1000); creditCard.SpendMoney += account.Update; creditCard.SpendMoney += sms.Update; creditCard.SpendAmount = 200; }