敏捷软件开发 – OBSERVER模式
数字时钟
我们有一个时钟对象。该对象捕获来自操作系统的毫秒终端(即时钟滴答),并把它们转换成时间。该队想知道如何从微秒数计算出秒数,如何从秒数计算出分钟数等等。它知道每个月有多少天,以及每年有几个月。它知道有关闰年的所有信息,并且可以判断出什么时候是闰年,而什么时候不是闰年。

我们想创建一个数字时钟,可以把它摆放在桌面上,并且连续地显示时间。最简单的实现方式如下。
public void DisplayTime()
{
while(true)
{
int sec = clock.Seconds;
int min = clock.Minutes;
int hour = clock.Hours;
ShowTime(hour, min, sec);
}
}
显然,这不是最好的方法。为了重复地显示时间,它消耗了所有可用的CPU周期。其中,大部分的显示都是多余的,因为时间没有变化。也许,这个解决方案可以应用于数字手表或者数字挂钟中,因为在这些系统中,节省CPU周期不是非常重要的。
OBSERVER模式
拉模型OBSERVER模式
Clock被DigitalClock观察。DigitalClock通过ISubject接口注册到Clock中。无论任何原因,只要时间一改变,Clock就调用ISubjec的Notify方法。而ISubject的Notify方法会调用每个已注册的IObserver对象的Update方法。因此,每当时间发生变化时,DigitalClock都会接收到一个Update消息。

OBSERVER模式是那种一旦你理解了,就会觉得导出都可以使用它的模式之一。这种间接关系非常好。你可以向各种对象注册观察者,而不用让这些对象显式的调用你。虽然这种间接关系是一种有用的管理依赖关系的方法,但是它很容易会被过分使用。过度的使用OBSERVER模式往往会导致系统难以理解和跟踪。
推模型OBSERVER模式
Notify方法和Update方法都带有一个参数。该参数是一个提示(hint)。它是通过Notify方法和Update方法从Employee传到SalaryObserver的。这个提示让SalaryObserver知道了雇员记录遭受了哪种变化。
Notify和Update的EmployeeObserverHint参数可能是某种枚举、一个字符串或者一个包含了某个字段新、旧值的复杂数据结构。不管它是什么,他的值都被推到观察者中。

要选择哪种OBSERVER模型完全取决于被观察对象的复杂性。如果被观察对象比较复杂,并且观察者需要一个提示,那么推模型是合适的。如果被观察对象比较简单,那么拉模型就很合适。
摘录自:[美]RobertC.Martin、MicahMartin著,邓辉、孙鸣译 敏捷软件开发原则、模式与实践(C#版修订版) [M]、人民邮电出版社,2013、351-367、

浙公网安备 33010602011771号