扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
一.引子:观察者模式中两个成员的对话
<<Head First Design Pattern>> 第二章中在讲到观察者模式的时候有段很经典的,是观察者模式中两个角色Subject跟Observer的对话,以下称Subject为S, Observer为O(不是完全按里面翻译的):
S:很高兴,为了改善咱两的关系,终于等到了今天这个机会跟Observer面对面地来交流。
O:哦,真的啊?我还以为您从来都没有太在意我们这些Observers啊。
S:呃,我一直都尽我所能做好我自己的工作啊,不是吗?每每在关键的时刻我都会告诉你们究竟是发生什么事了。。。我不认识你们中的每个人这样不意味着我是不在意你们的啊,而且对于你们来说,有一件最重要的事情我是知道的:你们都实现了Observer这样的接口,每当在关键时候,我告诉你们发生什么事了,你们就可以马上地去处理了。
O:嗯,确实是这样啊。但是那只是我们身上的一小部分啊,还有很多您是不知道的啊。无论怎样,我对您还是相当了解的。。。
S:哦,是吗?都了解哪些呢,不妨说来听听。
O:好的,比如说当您那边的情况一发生了什么变化,您始终都是把这些新“情报”第一时间地传达到我们中的每个Observer,这样我们就时刻都很清楚您那边是处于什么样的状态。但是有时这样也使我们感到很苦恼啊。。。
S:哦,对此我表示抱歉。我必须把我的状态送给我的通知器比如说notifyObservers()然后让它来通知你们,所以你们这些懒惰的Observers就知道发生了什么事了。
O:OK,等等,首先我们不是懒惰,因为您一旦通知我们事情有什么变化了,我们得在您Subject先生和您的通知器之间处理很多其它的事情啊;其次,为什么不让我们接近您呢,这样我们就可以很方便地拿到我们每个人真正所关心的那方面啊,而您总是把事情的方方面面都推到了我们每个人的面前,有些对我一点作用也没有。
S:嗯。。。我想那样会更起作用。跟你说这样的情况,你们Observers正在观察XX公司的一个具有一千多个字段的雇员信息,你们想让我在那些雇员们的某些属性发生变化后告诉你们,那你说在这种情况下我让你们进入我的个人世界,然后让你得到想要的,你们知道哪些属性发生变化吗,不可能的!也许是雇员的薪水变化了,也许是他们换了一个老板。这种情况下将可能是一件很危险的事情啊!所以说我不能让你们进来我的个人世界然后随你们探听察看我所拥有的一切。
O:那您为什么不留个公开的getter方法,从而我们就可以借助这个getter方法得到我们自己想要的那些啊?
S:是的,我确实可以让你们拉(pull)到我身上的状态。但是如果像你刚才所说的那样来做的话会更加方便吗?如果每次你都必须到进入我的个人世界然后拿走你们所想要的东西,这样的话你可能就得产生多个调用重复的getter方法来得所有你们想要的东西。这就是为什么我更喜欢把东西直接推(push)给你们的原因了。。。然后再个通知器里面你们就可以直接拿到所需的任何东西了。
O:不要做得这么的积极啊!在我们这些Observers中,各种各样的性格都有,你无法意料到我们需要的每件东西。就让我们进入您的个人世界,然后拿到我们所需要的东西。像这种方式的话,假如我们当中的某些Observers只是需要某些东西这样的话,这样的话我们就不会被迫接受给予的全部东西了。这样做也很方便以后地扩展啊。现在假设出现这种情况:由于各方面的原因,你壮大了自己,必须增加某种状态,如果是使用让我们自己拿(pull)的方式的话,您就不必为修改我们每个Observer的update方法,然后增加那个新来的东西这样的事情而到处奔波,您只需要在自己身上多写一个getter方法来支持这个新进来的状态就行了。
S:嗯,其实我能看出来无论是“推我”还是“拉你”都各有各的优点啊。我已经注意到了Java已经有了内置的对我们观察者模式的支持了,它可以允许你自己选择是要推我,还是拉你。
O:哦,真的?我想我要去感受下这种好处了。。。
S:嗯,好极了。。。我想我也要去看一个好的“拉你”的例子了,从而改变我的心态啊。。。
O:什么?我没听错吧,难道咱俩已经达到共识了?我想希望总是有的啊。。。
二 究竟是要“推我”还是“拉你”?
听完观察者模式两个成员的经典对话之后,我想大家对观察者模式会有了更深刻的印象。究竟是要“推我”还是“拉你”,从上面的对话我们很容易下这样结论:完全取决于被观察者对象的复杂性,如果被观察对象比较复杂,并且观察者需要有一个提示,那么推模型是合适的。如果被观察的对象比较简单,那么拉模型就很合适。
三 观察者模式在Java编程中的应用(重点学习JavaBeans事件模型)
学习设计模式的过程中,一直都有这种感受,看JDK的API甚至是源代码,你会发现比已经更有条理性了,更好理解了,可以说要想研究JDK源代码(我想别的源代码也一样)必须很好地理解了设计模式。有两种方式来查看观察者模式的细节,第一种办法是了解定义在java.util的Observer和Observable类。第二种方法是研究注册事件监听器的JavaBeans组件模型。在创建JavaBeans事件模型之前,Observer和Observable类就描述了观察者模式的实现。换句话说,自从Java平台1.0版本,这些类就已经存在了。由于这些类在设计上并没有什么技术错误,因此一直沿用下来了。这些类现在还可以用于实现观察者模式,但是用的更多的是JavaBeans事件模型,我也想花多点时间研究下在这方面的应用,这会对我们在GUI编程方面上有更多的帮助。
较喜欢这样的划分事件处理模型中的对象:事件对象,事件制造者对象和事件接收者对象。其中,某一个对象是事件的制造者,其余对象是事件的接收者;而事件对象本身则装了有关事件的信息。当事件制造者的内部状态发生了变化时,会根据需要创建一个代表其状变化的事件对象,并将它传给所有登记过的事件接收者对象。对于这种机制,我想大家在听了上面的对话后应该可以很自然地想到这就是建立在观察者模式的基础之上的。其实,从网上资料得知Java1.0的事件处理机制是建立在责任链模式的基础上的(呵呵,写这篇文章的时候还没有学到这种设计模式呢~~)在学习这种事件机制之前,我们先来看看MVC中的Model- View,它们作为MVC模式的一部分,就是Subject和Obverser的关系,因而,模型的改变必须要在UI对象中体现出来。Swing使用了 JavaBeans的事件模型来实现这种通知机制。根据前面Subject和Obverserr的对话,有两种实现办法,一是仅仅通知事件监听者状态改变了,然后由事件监听者向模型提取必要的状态信息。这种机制对于事件频繁的组件很有效。另外的一种办法是模型向监听者发送包含了已改变的状态信息的通知给 UI。这两种方法根据其优劣被分别是现在不同的组件中。比如在JScollBar中使用的是第一种方法,在JTable中使用的是第二种方法。顺便提下 JTable,我们在基于C/S架构的应用中如果要从数据库中取出一批数据然后把它大列表中显示,这时候会用到大量的JTable,到时候我们就可以更好地理解这种机制了。
回到事件监听机制,对Model而言,为了能够支持多个View,它并不知道具体的每一个View。它维护一个对其数据感兴趣的Obverser的列表。调用addXXXListener()方法增加一个监听器,调用removeXXXListener()方法删除一个监听器,这两个方法都要需要一个相应类型的监听器类型。所有的AWT组件都是java.awt.Component的子类,它们都从Component类继承了各个 addXXXListener()方法。
举个小例子:
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("监听器收到通知!");
}
};
JButton button = new JButton("单击我!");
button.addActionListener(listener);
在这个例子中,button包括了维护一个监听器聚集类,并提供各种管理监听器对象聚集的方法,比如addActionListener等,可以看出上面的listener就是一个监听器对象它实现了ActionListener接口的actionPerformed方法,这时如果用户单击了button,就会激发actionPerformed方法,其实这里的actionPerformed就相当于Observer中的update()方法。
在这里,我们实现一个监听器接口,将其附加到监听的对象上。监听的对象就是被监听的。它的职责是记住谁在监听,在JavaBeans组件模型中,用于附加和解除Observer对象的接口是添加和删除监听器命名模式。当监听对象的状态改变的时候,它会通知Observer对象。这种设计模式的一个主要目标就是将对象和观察者的耦合度降低。当JButton被选择的时候,并非调用一个叫做ButtonNotification类的特定方法,通知动作被抽象到一个接口中,任何类都可以实现它。JButton并不关心绑定的监听器是什么。事实上,按钮不关心实现类是不是被修改了。它关心的是观察者实现了这个监听器。
下面这段摘自网上的资料,和大家分享:
在使用观察者模式的时候,有很多的复杂问题需要注意。首先是可能出现内存泄露。监听的对象维护着一个观察者的引用。在监听对象释放这个引用之前,垃圾收集器都不能删除观察者。一定要清醒的认识到这种可能,在合适的时候删除掉观察者。另外需要注意的是观察者对象维持在一个无序的集合之中。至少当注册监听器的时候是这样。你没有必要知道先被注册的监听器是先被调用还是后被调用。如果你需要有序的调用,例如A必须先被调用然后是B。那么你必须引入一个中间层的对象来强制这种顺序。简单的按照顺序来注册监听器是不能确保调用顺序的。
四 总结
观察者模式是设计模式中的应用相当广泛的一种,希望本文能给大家学习观察者模式起到抛砖引玉的作用。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。