在現(xiàn)實(shí)世界中,許多對(duì)象并不是獨(dú)立存在的,其中一個(gè)對(duì)象的行為發(fā)生改變可能會(huì)導(dǎo)致一個(gè)或者多個(gè)其他對(duì)象的行為也發(fā)生改變。例如,某種商品的物價(jià)上漲時(shí)會(huì)導(dǎo)致部分商家高興,而消費(fèi)者傷心。 在軟件世界也是這樣,例如,事件模型中的事件源與事件處理者。所有這些,如果用觀察者模式來實(shí)現(xiàn)就非常方便。 定義與特點(diǎn)觀察者(Observer)模式的定義:指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。這種模式有時(shí)又稱作發(fā)布-訂閱模式、模型-視圖模式,它是對(duì)象行為型模式。 觀察者模式是一種對(duì)象行為型模式,其主要優(yōu)點(diǎn)如下:
它的主要缺點(diǎn)如下:
結(jié)構(gòu)與實(shí)現(xiàn)實(shí)現(xiàn)觀察者模式時(shí)要注意具體目標(biāo)對(duì)象和具體觀察者對(duì)象之間不能直接調(diào)用,否則將使兩者之間緊密耦合起來,這違反了面向?qū)ο蟮脑O(shè)計(jì)原則。 模式的結(jié)構(gòu)觀察者模式的主要角色如下:
觀察者模式的結(jié)構(gòu)圖如圖所示: 模式的實(shí)現(xiàn)觀察者模式的實(shí)現(xiàn)代碼如下: class Program { static void Main(string[] args) { Subject subject=new ConcreteSubject(); IObserver obs1=new ConcreteObserver1(); IObserver obs2=new ConcreteObserver2(); subject.Add(obs1); subject.Add(obs2); subject.NotifyObserver(); Console.Read(); } } //抽象目標(biāo) public abstract class Subject { protected List<IObserver> observers=new List<IObserver>(); //增加觀察者方法 public void Add(IObserver observer) { observers.Add(observer); } //刪除觀察者方法 public void Remove(IObserver observer) { observers.Remove(observer); } public abstract void NotifyObserver(); //通知觀察者方法 } //具體目標(biāo) public class ConcreteSubject : Subject { public override void NotifyObserver() { Console.WriteLine("具體目標(biāo)發(fā)生改變..."); Console.WriteLine("--------------"); foreach (var obs in observers) { obs.Response(); } } } //抽象觀察者 public interface IObserver { void Response(); //反應(yīng) } //具體觀察者1 public class ConcreteObserver1 : IObserver { public void Response() { Console.WriteLine("具體觀察者1作出反應(yīng)!"); } } //具體觀察者1 public class ConcreteObserver2 : IObserver { public void Response() { Console.WriteLine("具體觀察者2作出反應(yīng)!"); } } 程序運(yùn)行結(jié)果如下: 具體目標(biāo)發(fā)生改變... -------------- 具體觀察者1作出反應(yīng)! 具體觀察者2作出反應(yīng)! 應(yīng)用場景通過前面的分析與應(yīng)用實(shí)例可知觀察者模式適合以下幾種情形:
擴(kuò)展:.net中的IObservable和 IObserver接口在.net環(huán)境下,其運(yùn)行時(shí)庫為開發(fā)者提供了IObservable和 IObserver接口,用于實(shí)現(xiàn)觀察者模式軟件設(shè)計(jì)。另外,ObservableCollection 類表示一個(gè)動(dòng)態(tài)數(shù)據(jù)集合,它可在添加、刪除項(xiàng)目或刷新整個(gè)列表時(shí)提供通知。
注:在 Java 中,通過 java.util.Observable 類和 java.util.Observer 接口定義了觀察者模式,只要實(shí)現(xiàn)它們的子類就可以編寫觀察者模式實(shí)例。 下面的示例演示觀察者設(shè)計(jì)模式,實(shí)現(xiàn)定位系統(tǒng)實(shí)時(shí)通知當(dāng)前經(jīng)緯度坐標(biāo),代碼如下: class Program { static void Main(string[] args) { // 定義一個(gè)提供者和兩個(gè)觀察者 LocationTracker provider = new LocationTracker(); LocationReporter reporter1 = new LocationReporter("FixedGPS"); reporter1.Subscribe(provider); LocationReporter reporter2 = new LocationReporter("MobileGPS"); reporter2.Subscribe(provider); provider.TrackLocation(new Location(47.6456, -122.1312)); reporter1.Unsubscribe(); provider.TrackLocation(new Location(47.6677, -122.1199)); provider.TrackLocation(null); provider.EndTransmission(); Console.Read(); } } /// <summary> /// 位置:包含緯度和經(jīng)度信息 /// </summary> public struct Location { double lat, lon; public Location(double latitude, double longitude) { this.lat = latitude; this.lon = longitude; } /// <summary> /// 緯度 /// </summary> public double Latitude { get { return this.lat; } } /// <summary> /// 經(jīng)度 /// </summary> public double Longitude { get { return this.lon; } } } /// <summary> /// 位置報(bào)告者:提供 IObserver<T> 實(shí)現(xiàn),它顯示有關(guān)當(dāng)前控制臺(tái)位置的信息 /// </summary> public class LocationReporter : IObserver<Location> { private IDisposable unsubscriber; private string instName; public LocationReporter(string name) { this.instName = name; } public string Name { get { return this.instName; } } /// <summary> /// 訂閱:將由對(duì) Subscribe 的調(diào)用返回的 IDisposable 實(shí)現(xiàn)保存到私有變量中 /// </summary> /// <param name="provider"></param> public virtual void Subscribe(IObservable<Location> provider) { if (provider != null) unsubscriber = provider.Subscribe(this); } public virtual void OnCompleted() { Console.WriteLine("位置跟蹤器已將數(shù)據(jù)傳輸?shù)?nbsp;{0}", this.Name); this.Unsubscribe(); } public virtual void OnError(Exception e) { Console.WriteLine("{0}: 無法確定位置", this.Name); } public virtual void OnNext(Location value) { Console.WriteLine("{2}: 當(dāng)前位置是 {0}, {1}", value.Latitude, value.Longitude, this.Name); } /// <summary> /// 退訂:使類可以通過調(diào)用提供程序的 Dispose 實(shí)現(xiàn)來取消訂閱通知 /// </summary> public virtual void Unsubscribe() { unsubscriber.Dispose(); } } /// <summary> /// 位置跟蹤器:提供 IObservable<T> 實(shí)現(xiàn) /// </summary> public class LocationTracker : IObservable<Location> { public LocationTracker() { observers = new List<IObserver<Location>>(); } private List<IObserver<Location>> observers; /// <summary> /// 訂閱:某觀察程序?qū)⒁邮胀ㄖ? /// </summary> /// <param name="observer"></param> /// <returns></returns> public IDisposable Subscribe(IObserver<Location> observer) { if (!observers.Contains(observer)) observers.Add(observer); return new Unsubscriber(observers, observer); } /// <summary> /// IDisposable 實(shí)現(xiàn):用于刪除觀察者或取消訂閱 /// </summary> private class Unsubscriber : IDisposable { private List<IObserver<Location>> _observers; private IObserver<Location> _observer; public Unsubscriber(List<IObserver<Location>> observers, IObserver<Location> observer) { this._observers = observers; this._observer = observer; } public void Dispose() { if (_observer != null && _observers.Contains(_observer)) _observers.Remove(_observer); } } public void TrackLocation(Nullable<Location> loc) { foreach (var observer in observers) { if (!loc.HasValue) observer.OnError(new LocationUnknownException()); else observer.OnNext(loc.Value); } } public void EndTransmission() { foreach (var observer in observers.ToArray()) { if (observers.Contains(observer)) observer.OnCompleted(); } observers.Clear(); } } /// <summary> /// 位置未知異常 /// </summary> public class LocationUnknownException : Exception { internal LocationUnknownException() { } } 程序運(yùn)行結(jié)果如下: FixedGPS:當(dāng)前位置是47.6456,-122.1312 MobileGPS:當(dāng)前位置是47.6456,-122.1312 MobileGPS:當(dāng)前位置是47.6677,-122.1199 MobileGPS:無法確定位置位置 跟蹤器已將數(shù)據(jù)傳輸?shù)組obileGPS |
|