首页 理论教育 观察者模式-大话设计模式中的第九章

观察者模式-大话设计模式中的第九章

时间:2023-11-03 理论教育 版权反馈
【摘要】:第九章放风者与偷窃者——观察者模式9.1放风者与偷窃者时间:12月25日地点:大B房间人物:大B,小A这天,小A又到大B家玩,闲聊时他们说起了警匪片。放上观察者模式的类图,如图9-1所示。

观察者模式-大话设计模式中的第九章

第九章 放风者与偷窃者——观察者模式

9.1 放风者与偷窃者

时间:12月25日  地点:大B房间  人物:大B,小A

这天,小A又到大B家玩,闲聊时他们说起了警匪片

大B:“你喜欢看警匪片吗?”

小A:“嘿嘿!很喜欢。”

大B:“那你还记得警匪片上,匪徒们是怎么配合实施犯罪的吗?”

小A:“我知道他们总是会有放风者,还有就是所谓的做案者。”

大B:“嗯。对!一个团伙在进行盗窃的时候,总有一两个人在门口把风——如果有什么风吹草动,则会立即通知里面的同伙紧急撤退。也许放风的人并不一定认识里面的每一个同伙;而在里面也许有新来的小弟不认识这个放风的。但是这没什么,这个影响不了他们之间的通讯,因为他们之间有早已商定好的暗号。”

小A:“是啊!那个情节是最精彩的时候,我最喜欢看了。”

大B:“看警匪片的时候最好看的就是那个片段了。”

9.2 观察者模式

大B:“你知道什么样的是属于观察者模式吗?”

小A:“是不是有观察者和被观察者的就是属于观察者模式?”

大B:“上面提到的放风者、偷窃者之间的关系就是观察者模式在现实中的活生生的例子。你现在知道什么是观察者模式了吧?”

小A:“嘿嘿!还不能完全理解。”

大B:“观察者模式又名发布订阅模式。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。”

大B:“当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节,观察者模式提供了一种对象设计,让主题和观察者之间松耦合。”

小A:“为什么呢?”

大B:“1、关于观察者的一切,主题只知道观察者实现了某个接口,不需要知道观察者的具体类是谁2、任何时候我们都可以增加新的观察者,因为主题唯一依赖的东西是一个实现Observer接口的对象列表3、有新类型的观察者出现时,主题的代码不需要修改。只要在新类里实现观察者接口,然后注册为观察者即可4、我们可以独立的复用主题或观察者,因为2者并非紧耦合5、改变主题或观察者任何一方,并不会影响另一方,只要他们之间的接口人被遵守。”

9.3 组成部分

小A:“那它的组成部分哩?是不是就是有观察者和被观察者的就是属于观察者模式?”

大B:“不是。1、抽象目标角色:目标角色知道它的观察者,可以有任意多个观察者观察同一个目标。并且提供注册和删除观察者对象的接口。目标角色往往由抽象类或者接口来实现。2、抽象观察者角色:为那些在目标发生改变时需要获得通知的对象定义一个更新接口。抽象观察者角色主要由抽象类或者接口来实现。3、具体目标角色:将有关状态存入各个具体观察者角色对象。当它的状态发生改变时, 向它的各个观察者发出通知。4、具体观察者角色:存储有关状态,这些状态应与目标的状态保持一致。实现观察者角色的更新接口以使自身状态与目标的状态保持一致。在这个角色内也可以维护一个指向具体目标角色对象的引用。”

放上观察者模式的类图,如图9-1所示。这样能将关系清晰的表达出来。

图9-1 观察者模式的类图

9.4 天气预报

大B:“用一个实际的例子来说明:日常生活中说起观察者,最常见的例子可能就是天气预报,在这里我们的观察对象是地球,而我们是通过发射气象卫星这个观察者来检测地球气象变化的。”

所以这个例子中涉及三个对象:

地球(Earth):被观察对象

气象卫星(Satellite):观察者

气象局(WeatherService):客户端调用

被观察对象:地球 (Earth)

[注意] 在需检测的对象前需要设置变化点setChanged()和通知观察者notifyObservers(),这两个函数是由Observable类实现的,封装了观察者模式实现的细节。

观察者:气象卫星(Satellite)

客户端调用:气象局(WeatherService)

[运行结果]

天气预报:

------------

近期天气变化:台风珍珠’逼近

近期天气变化:大到暴雨

近期天气变化:天气炎热

9.5 观察者模式原理

小A:“观察者模式原理是什么?”

大B:“主题对象并不知道引用了哪些具体观察者对象类型,而只知道抽象观察者类型,这样具体主题对象可以动态地维护一系列的观察者对象的引用,并在需要的时候调用每一个观察者共有的更新方法。这是‘针对接口编程’的体现。”

9.6 现实生活中的观察者

在现实世界中,观察者模式对于那种由许多JavaScript程序员合作开发的大型程序特别有用。它可以提高API的灵活性,使并行开发的多个实现能够彼此独立地进行修改。(www.xing528.com)

大B:“作为开发人员,你可以对自己的应用程序中什么是‘令人感兴趣的时刻’做出决定。你所能监听的不再只是click、load、blur和mouseover等浏览器事件。在用户界面(rich UI)应用程序中,drag(拖动)、drop(拖放)、moved(移动)、complete(完成)和tabSwitch(标签切换)都可能是令人感兴趣的事件。它们都是在普通浏览器事件的基础上抽象出来的可观察事件,可由发布者对象向其监听者广播。”

9.7 事件监听器也是观察者

大B:“在DOM脚本编程环境中的高级事件模式中,事件监听器说到底就是一种内置的观察者。事件处理器(handler)与事件监听器(listener)并不是一回事。事件处理器说穿了就是一种把事件传给与其关联的函数的手段。而且在这种模型中一种事件只能指定一个回调方法。而在监听器模式中,一个事件可以与几个监听器关联。每个监听器都能独立于其他监听器而改变。”

小A:“师兄,能不能再细说一下?”

大B:“可以,我举个例子,你应该就可以明白了。打个比方,对San Francisco Chronicle这家报社来说,其订阅者Joe订没订New York Times都无所谓。同样,Joe也不在乎Lindsay是否也订了San Francisco Chronnicle。每一方都只管处理自己的数据和相关的行为。”

例如,使用事件监听器,可以让多个函数响应同一个事件:

但用事件处理器就办不到:

大B:“在第一个例子中,由于使用的是事件监听器,所以click事件发生时fn1和fn2都会被调用。而第二个例子使用的是事件处理器,其中第二次对onclick赋值的结果是fn1被fn2取代,因此click事件发生时只会调用fn2,不会调用fn1。言归正传。监听器和观察者之间的共同之处显而易见。实际上它们互为同义语。它们都订阅特定的事件,然后等待事件的发生。事件发生时,订阅方的回调函数会得到通知。传给它们的参数是一个事件对象,其中包含着事件发生时间、事件类型和事件发源地等有用的信息。”

小A:“这样我就明白了。”

9.8 观察者模式的典型应用

大B:“我再和你说说观察者模式的典型应用。”

小A:“好。”

大B:“1、侦听事件驱动程序设计中的外部事件2、侦听/监视某个对象的状态变化3、发布者/订阅者(publisher/subscriber)模型中,当一个外部事件(新的产品,消息的出现等等)被触发时,通知邮件列表中的订阅者。”

9.9 观察者模式的优点

小A:“那观察者模式有什么优点呢?”

大B:“对象之间可以进行同步通信,可以同时通知一到多个关联对象,对象之间的关系以松耦合的形式组合,互不依赖。”

9.10 使用情况

大B:“讲了这么多,你现在能说说观察者模式的使用情况吗?”

小A:“1、当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。2、当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。3、当一个对象必须通知其它对象,而它又不能假定其它对象是谁。”

大B:“换言之, 你不希望这些对象是紧密耦合的。其实观察者模式同前面讲过的桥梁、策略有着共同的使用环境:将变化独立封装起来,以达到最大的重用和解耦。观察者与后两者不同的地方在于,观察者模式中的目标和观察者的变化不是独立的,而是有着某些联系。”

9.11 我推你拉

大B:“观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本。”

小A:“哪两种版本呐?”

大B:“一种情况是目标角色在发生变化后,仅仅告诉观察者角色‘我变化了’;观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式——就是说变化的信息是观察者角色主动从目标角色中‘拉’出来的。还有一种方法,那就是我目标角色‘服务一条龙’,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是‘推模式’——管你要不要,先给你啦。这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则‘推模式’比较合适。如果目标角色比较简单,则‘拉模式’就很合适啦。”

9.12 圣斗士星矢的状态模式和观察者模式

小A:“讲了那么多,师兄如果能给我举个例子那就更好了。”

大B:“那我给你举个圣斗士星矢的状态模式和观察者模式的例子吧!”

星矢:动画片《圣斗士星矢》的男猪蹄,超级小强,怎么打也打不死。

雅典娜:动画片《圣斗士星矢》的女猪蹄,自称女神,手下有88个男人为他卖命。

状态模式:为了方便的控制状态的变化,避免一堆IF/ELSE,以及状态规则改变的时避免代码改动的混乱。

观察者模式:一个被观察者一动,多个观察者跟着动,经常用于界面UI。

话说星矢和很强的某斗士甲对打,雅典娜在一边看,星矢总是挨揍,每次挨揍完之后星矢的状态总是会发生一些变化:

正常--挨打--瀕死--挨打--小宇宙爆发--挨打--瀕死--挨打--女神护体--挨打(星矢无敌了,打也没用,战斗结束)--正常

以上状态转变用状态模式来表现,一个Saiya类代表星矢,一个SaiyaState代表他的状态,SaiyaState下面有多个子类,分别代表星矢的多种状态,如正常NORMAL、瀕死DYING、小宇宙爆发UNIVERSE、女神护体GODDESS,即把状态抽象成对象,在每种状态里面实现被打的时候所需要更改的状态,这样就避免了每次被打都要进行一次IF/ELSE的判断。

Java代码

在每种状态里面实现被打的时候所需要更改的状态,例如小宇宙爆发状态下被打

Java代码

雅典娜在一边看,星矢每次被打她都要给星矢加油,她是个观察者,星矢是被观察者,这里星矢实现java.util.Observable,每次被打hit就notifyObservers,雅典娜就加油。

Java代码

总的来看这个过程就是这样子:

Java代码

结果星矢在雅典娜的帮助下,有惊无险的战胜了很强的某斗士甲:

Java代码

总结:状态模式的缺点就是会弄出很多子类,如果状态没那么复杂,状态规则改变的可能性比较小的话就不要用了。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈