目錄
- 定義與特點
- 結構與實作
- 模式的結構
- 模式的實作
- 應用場景
- 擴展:.net中的IObservable和 IObserver介面
在現實世界中,許多物件并不是獨立存在的,其中一個物件的行為發生改變可能會導致一個或者多個其他物件的行為也發生改變,例如,某種商品的物價上漲時會導致部分商家高興,而消費者傷心,
在軟體世界也是這樣,例如,事件模型中的事件源與事件處理者,所有這些,如果用觀察者模式來實作就非常方便,
定義與特點
觀察者(Observer)模式的定義:指多個物件間存在一對多的依賴關系,當一個物件的狀態發生改變時,所有依賴于它的物件都得到通知并被自動更新,這種模式有時又稱作發布-訂閱模式、模型-視圖模式,它是物件行為型模式,
觀察者模式是一種物件行為型模式,其主要優點如下:
- 降低了目標與觀察者之間的耦合關系,兩者之間是抽象耦合關系,
- 目標與觀察者之間建立了一套觸發機制,
它的主要缺點如下:
- 目標與觀察者之間的依賴關系并沒有完全解除,而且有可能出現回圈參考,
- 當觀察者物件很多時,通知的發布會花費很多時間,影響程式的效率,
結構與實作
實作觀察者模式時要注意具體目標物件和具體觀察者物件之間不能直接呼叫,否則將使兩者之間緊密耦合起來,這違反了面向物件的設計原則,
模式的結構
觀察者模式的主要角色如下:
- 抽象主題(Subject)角色:也叫抽象目標類,它提供了一個用于保存觀察者物件的聚集類和增加、洗掉觀察者物件的方法,以及通知所有觀察者的抽象方法,
- 具體主題(Concrete Subject)角色:也叫具體目標類,它實作抽象目標中的通知方法,當具體主題的內部狀態發生改變時,通知所有注冊過的觀察者物件,
- 抽象觀察者(Observer)角色:它是一個抽象類或介面,它包含了一個更新自己的抽象方法,當接到具體主題的更改通知時被呼叫,
- 具體觀察者(Concrete Observer)角色:實作抽象觀察者中定義的抽象方法,以便在得到目標的更改通知時更新自身的狀態,
觀察者模式的結構圖如圖所示:

模式的實作
觀察者模式的實作代碼如下:
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();
}
}
//抽象目標
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(); //通知觀察者方法
}
//具體目標
public class ConcreteSubject : Subject
{
public override void NotifyObserver()
{
Console.WriteLine("具體目標發生改變...");
Console.WriteLine("--------------");
foreach (var obs in observers)
{
obs.Response();
}
}
}
//抽象觀察者
public interface IObserver
{
void Response(); //反應
}
//具體觀察者1
public class ConcreteObserver1 : IObserver
{
public void Response()
{
Console.WriteLine("具體觀察者1作出反應!");
}
}
//具體觀察者1
public class ConcreteObserver2 : IObserver
{
public void Response()
{
Console.WriteLine("具體觀察者2作出反應!");
}
}
程式運行結果如下:
具體目標發生改變...
--------------
具體觀察者1作出反應!
具體觀察者2作出反應!
應用場景
通過前面的分析與應用實體可知觀察者模式適合以下幾種情形:
- 物件間存在一對多關系,一個物件的狀態發生改變會影響其他物件,
- 當一個抽象模型有兩個方面,其中一個方面依賴于另一方面時,可將這二者封裝在獨立的物件中以使它們可以各自獨立地改變和復用,
擴展:.net中的IObservable和 IObserver介面
在.net環境下,其運行時庫為開發者提供了IObservable
- 提供者或主題,是將通知發送給觀察者的物件, 提供程式是實作IObservable
介面的類或結構 , 提供者必須實作單個方法IObservable.Subscribe ,該方法由希望從提供者接收通知的觀察者呼叫,- 觀察者,即從提供程式接收通知的物件, 觀察者是實作 IObserver
介面的類或結構 , 觀察者必須實作以下三個方法,這三個方法均由提供程式呼叫:
IObserver.OnNext ,它向觀察者提供新資訊或當前資訊,
IObserver.OnError ,它通知觀察者已發生錯誤,
IObserver.OnCompleted ,它指示提供程式已完成發送通知,- 允許提供程式跟蹤觀察者的一種機制, 通常情況下,提供程式使用容器物件(如 List
物件)來保存對已訂閱通知的 IObserver , 將存盤容器用于此目的使提供程式能夠處理零到無限數量的觀察者, 未定義觀察者接收通知的順序;提供程式可以隨意使用任何方法來確定順序,實作的參考 - IDisposable 實作,它使提供程式在能夠通知完成時洗掉觀察者, 觀察者從 Subscribe 方法接收對 IDisposable 實作的參考,因此它們還可以呼叫 IDisposable.Dispose 方法,以便在提供程式已完成發送通知之前取消訂閱,
- 包含提供程式發送到其觀察者的資料的物件, 此物件的型別對應 IObservable
和 IObserver , 盡管此物件可與 IObservable介面的泛型型別引數 實作相同,但通常情況下,它是一個單獨的型別,
注:在 Java 中,通過 java.util.Observable 類和 java.util.Observer 介面定義了觀察者模式,只要實作它們的子類就可以撰寫觀察者模式實體,
下面的示例演示觀察者設計模式,實作定位系統實時通知當前經緯度坐標,代碼如下:
class Program
{
static void Main(string[] args)
{
// 定義一個提供者和兩個觀察者
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>
/// 位置:包含緯度和經度資訊
/// </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>
/// 經度
/// </summary>
public double Longitude
{ get { return this.lon; } }
}
/// <summary>
/// 位置報告者:提供 IObserver<T> 實作,它顯示有關當前控制臺位置的資訊
/// </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>
/// 訂閱:將由對 Subscribe 的呼叫回傳的 IDisposable 實作保存到私有變數中
/// </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("位置跟蹤器已將資料傳輸到 {0}", this.Name);
this.Unsubscribe();
}
public virtual void one rror(Exception e)
{
Console.WriteLine("{0}: 無法確定位置", this.Name);
}
public virtual void OnNext(Location value)
{
Console.WriteLine("{2}: 當前位置是 {0}, {1}", value.Latitude, value.Longitude, this.Name);
}
/// <summary>
/// 退訂:使類可以通過呼叫提供程式的 Dispose 實作來取消訂閱通知
/// </summary>
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
/// <summary>
/// 位置跟蹤器:提供 IObservable<T> 實作
/// </summary>
public class LocationTracker : IObservable<Location>
{
public LocationTracker()
{
observers = new List<IObserver<Location>>();
}
private List<IObserver<Location>> observers;
/// <summary>
/// 訂閱:某觀察程式將要接收通知
/// </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 實作:用于洗掉觀察者或取消訂閱
/// </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()
{ }
}
程式運行結果如下:
FixedGPS:當前位置是47.6456,-122.1312
MobileGPS:當前位置是47.6456,-122.1312
MobileGPS:當前位置是47.6677,-122.1199
MobileGPS:無法確定位置位置
跟蹤器已將資料傳輸到MobileGPS
參考資料:
觀察者設計模式——MSDN
ObservableCollection
IObservable
IObserver
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/1066.html
標籤:設計模式
上一篇:設計模式-結構型模式總結
下一篇:行為型模式之中介者模式
