虽然观察者模式有很多变体,但该模式的基本前提包含两个角色:观察者和主体(熟悉 Smalltalk MVC 的人将这些术语分别称为视图和模型)。 在用户界面的环境中,观察者是负责向用户显示数据的对象。 另一方面,主体表示从问题域中模拟的业务抽象。 正如图 1 中所描述的一样,在观察者和主体之间存在逻辑关联。 当主体对象中发生更改时,(例如,修改实例变量),观察者就会观察 这种更改,并相应地更新其显示。
Observer ----observes----à Subject
观察者模式的步骤:观察者注册主体,表明它对观察的意向。 在某种状态发生变化时,主体向观察者通知这种变化情况。 当观察者不再希望观察主体时,观察者从主体中撤消注册。 这些步骤分别称为观察者注册、通知和撤消注册。
下面是两个接口:
//interface the all observer classes should implement
public interface IObserver
{
void Notify(object anObject);
}
//interface that all observable classes should implement
public interface IObservable
{
void Register(IObserver anObserver);
void UnRegister(IObserver anObserver);
}
除了这些接口外,框架还经常为主体提供一个用于扩展的通用基类。 此基类扩展减少了支持观察者模式所需的工作。 基类实现 IObservable 接口,以提供支持观察者实例存储和通知所需的基础结构。
尽管可能任何容器都可以完成这一任务,但该类在 Register 和 UnRegister 方法中将观察者存储委派给哈希表实例(为了方便起见,我们在示例中使用哈希表作为容器,它只使用一个方法调用来撤消注册特定的观察者实例)。还要注意添加了 NotifyObservers 方法。 此方法用于通知哈希表中存储的观察者。 在调用此方法时,将枚举该容器,并对观察者实例调用 “通知” 方法。
//helper class that implements observable interface
public class ObservableImpl : IObservable
{
//container to store the observer instance
protected Hashtable _observerContainer = new Hashtable();
//add the observer
public void Register(IObserver anObserver)
{
_observerContainer.Add(anObserver, anObserver);
}
//remove the observer
public void UnRegister(IObserver anObserver)
{
_observerContainer.Remove(anObserver);
}
//common method to notify all the observers
public void NotifyObservers(object anObject)
{
//enumeration the observers and invoke their notify method
foreach (IObserver anObserver in _observerContainer.Keys)
{
anObserver.Notify(anObject);
}
}
}
下面是主体类,表示股票,它只有一个属性表示当前的价格,当价格发生变化时,自动的通知观察者:
//represents a stock in an application
public class Stock : ObservableImpl
{
//instance variable for ask price
object _askPrice;
//property for ask price
public object AskPrice
{
set
{
_askPrice = value;
base.NotifyObservers(_askPrice);
}
}
}
下面是观察者,用于向用户显示这个变化:
//represents the user interface in the application
public class StockDisplay : IObserver
{
public void Notify(object anObject)
{
Console.WriteLine("The new ask price is:" + anObject);
}
}
最后是启动的测试代码:
public class MainClass
{
public static void Main()
{
//create new display and stock instances
StockDisplay stockDisplay = new StockDisplay();
Stock stock = new Stock();
//register the grid
stock.Register(stockDisplay);
//loop 100 times and modify the ask price
for (int looper = 0; looper < 100; looper++)
{
stock.AskPrice = looper;
}
//unregister the display
stock.UnRegister(stockDisplay);
}
}