"Head First"设计模式读书笔记——观察者模式
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
问题描述:
观察者日报需要新增一个电子版报纸系统,要求订阅电子版报纸的用户可以实时的获取最新报纸。
系统需要有注册用户和注销用户功能。当有最新的报纸出版时,所有订阅了报纸的用户可以自动获得最新版报纸。
系统设计:
观察者模式用于定义对象之间的一对多依赖,当一个对象状态改变的时候,其他对其依 赖的对象都能收到改变后的状态。在“观察者日报”所要求的系统中,正好符合这一定义。出版者和订阅者之间的关系为一对多依赖,每当有新的报纸出版(出版者 的状态改变了),所有的订阅者都可以收到并显示最新的报纸(收到出版者的最新状态并自动更新)
系统设计的第一步是设计出版者超类型和订阅者超类型,两者各自确立相应的接口。出版者超类型(Subject)和订阅者超类型(Observer)定义如下图:
代码实现:
{
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
interface Observer
{
void update(Newspaper n);
void display();
}
其中Subject接口定义了registerObserver和removeObserver方法,用于注册和注销订阅者、notifyObservers方法用于向订阅者发送最新的报纸。Observer接口定义了update方法,用于更新自己的报纸、display方法用于显示自己收到的报纸。
Newspaper类型的代码实现如下:
任何一个出版者必须实现Subject接口,观察者日报办公室定义如下:
代码实现:
NewspaperOffice类中含有一个Newspaper类型的变量,用于表示当前最新的报纸、一个Observers列表,用于记录当前所有的订阅者。
“观察者日报”最初是面向中国地区的,有许多中国订阅者,定义如下:
代码实现:
Chinese类中含有一个newspaper变量,用于标示自己当前收到的报纸。
“观察者日报”采用新的系统后大获成功,超人感觉应该让所有氪星人人手一份,以增 长见识,所以又来了许多氪星订阅者。这时问题出现了,日报没有客星语言版的,怎么办呢,为了让所有氪星人都能读懂报纸,必须在氪星订阅者中添加一个翻译方 法,将中文版的“观察者日报”翻译成氪星版日报。氪星订阅者的定义如下:
代码实现:
为“观察者日报”添加一Chinese类型订阅者,和一Kryptonies订阅者,当有新的报纸出版的时候,所有的订阅者会自动更新并显示最新报纸。Kryptonies订阅者会自行将收到的报纸翻译成氪星语,出版者只负责发送报纸,不必管,也不必知道订阅者如何处理收到的报纸。系统类图如下:
测试扩展性:
现在来测试一下系统的扩展性,“观察者日报”最近新推出了一个版面——“身边的特异功能者”,Sylar发现了这个版面后兴奋的三天没合眼,有了这个版面,再也不需要追着Mohinder博士抢什么地图了,只要每天订阅这个报纸就可以轻而易举的找到那些超能者,挖开他们的大脑,获得他们的能力。Sylar立即订阅了一份“观察者日报”。
Sylar的定义如下:
代码实现:
Sylar很快将自己注册成订阅者中的一员。现在Sylar也能不断收到最新的报纸了。那些超能者要小心了。增加Sylar后,系统类图如下:
Sylar发现自己只需要“观察者日报”的一个版面,其他都是累赘。于是他决定收到报纸后只剪下自己需要的板块,其他的都丢掉。改进Sylar类,增加剪报功能。改进后的Sylar类图如下:
现在Sylar不但能收到最新的报纸,还能在收到后自行决定只显示某一个版面。
由此可见观察者模式具有很好的可扩展性。订阅者只要实现了Observer接口就可以成为一个“观察者”,而出版者只要实现了Subject接口就可以成为一个“主题”。至于两者中对方内部是怎样实现的,各自完全不必理会。出版者只管将最新报纸(状态)发送出去,而订阅者收到最新报纸(状态)后完全可以自行决定如何处理而不会影响到其他的订阅者。
总结:
文中的观察者模式采用的是“推”的方式,只要有新的状态,主题(出版者)就会将最 新的状态发送给观察者(订阅者)。还有另外一种“拉”的方式。在“拉”的方式下由观察者(订阅者)决定何时要新的状态而不必每次都被动的接受主题(出版 者)发来的状态。不过在这种状态下主题(出版者)必须定义一系列的getter方法来get相应的状态,因此而门户打开,有一定的安全隐患。读者可自行实现。