简单地说,观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化可以通知全部的依赖于此对象的那些观察者对象,使这些观察者对象可以自动更新。
观察者模式的结构
观察者(Observer)模式是对象的行为型模式,又叫作发表-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-收听者(Source/Listener)模式或从属者(Dependents)模式。
本模式的类图结构以下:
java
![]() 图一、观察者模式的静态结构可从类图中看清楚。 |
在观察者模式里有以下的角色:
. 抽象主题(Subject)角色:主题角色把全部的观察者对象的引用保存在一个列表里;每一个主题均可以有任何数量的观察者。主题提供一个接口能够加上或撤销观察者对象;主题角色又叫作抽象被观察者(Observable)角色;
框架
![]() 图二、抽象主题角色,有时又叫作抽象被观察者角色,能够用一个抽象类或者一个接口实现;在具体的状况下也不排除使用具体类实现。 |
. 抽象观察者(Observer)角色:为全部的具体观察者定义一个接口,在获得通知时更新本身;
异步
![]() 图三、抽象观察者角色,能够用一个抽象类或者一个接口实现;在具体的状况下也不排除使用具体类实现。 |
. 具体主题(ConcreteSubject)角色:保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫做具体被观察者角色;
ide
![]() 图四、具体主题角色,一般用一个具体子类实现。 |
.具体观察者(ConcreteObserver)角色:保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新本身的接口,以便使自己的状态与主题的状态自恰。
this
![]() 图五、具体观察者角色,一般用一个具体子类实现。 |
下面给出一个示意性实现的Java代码。首先在这个示意性的实现里,用一个Java接口实现抽象主题角色,这就是下面的Subject接口:
线程
public interface Subject { public void attach(Observer observer); public void detach(Observer observer); void notifyObservers(); } |
import java.util.Vector; import java.util.Enumeration; public class ConcreteSubject implements Subject { public void attach(Observer observer) { observersVector.addElement(observer); } public void detach(Observer observer) { observersVector.removeElement(observer); } public void notifyObservers() { Enumeration enumeration = observers(); while (enumeration.hasMoreElements()) { ((Observer)enumeration.nextElement()).update(); } } public Enumeration observers() { return ((Vector) observersVector.clone()).elements(); } private Vector observersVector = new java.util.Vector(); } |
public interface Observer { void update(); } |
public class ConcreteObserver implements Observer { public void update() { // Write your code here } } |
![]() 图六、java.util提供的Observer接口的类图。 |
package java.util; public interface Observer { /** * 当被观察的对象发生变化时,这个方法会被调用。 */ void update(Observable o, Object arg); } |
![]() 图七、Java语言提供的被观察者的类图。 |
package java.util; public class Observable { private boolean changed = false; private Vector obs; /** 用0个观察者构造一个被观察者。**/ public Observable() { obs = new Vector(); } /** * 将一个观察者加到观察者列表上面。 */ public synchronized void addObserver(Observer o) { if (!obs.contains(o)) { obs.addElement(o); } } /** * 将一个观察者对象从观察者列表上删除。 */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** * 至关于 notifyObservers(null) */ public void notifyObservers() { notifyObservers(null); } /** * 若是本对象有变化(那时hasChanged 方法会返回true) * 调用本方法通知全部登记在案的观察者,即调用它们的update()方法, * 传入this和arg做为参量。 */ public void notifyObservers(Object arg) { /** * 临时存放当前的观察者的状态。参见备忘录模式。 */ Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** * 将观察者列表清空 */ public synchronized void deleteObservers() { obs.removeAllElements(); } /** * 将“已变化”设为true */ protected synchronized void setChanged() { changed = true; } /** * 将“已变化”重置为false */ protected synchronized void clearChanged() { changed = false; } /** * 探测本对象是否已变化 */ public synchronized boolean hasChanged() { return changed; } /** * 返还被观察对象(即此对象)的观察者总数。 */ public synchronized int countObservers() { return obs.size(); } } |
![]() 图八、使用Java语言提供的对观察者模式的支持。 |
![]() 图九、一个使用Observer接口和Observable类的例子。 |
package com.javapatterns.observer.watching; import java.util.Observer; public class Tester { static private Watched watched; static private Observer watcher; public static void main(String[] args) { watched = new Watched(); watcher = new Watcher(watched); watched.changeData("In C, we create bugs."); watched.changeData("In Java, we inherit bugs."); watched.changeData("In Java, we inherit bugs."); watched.changeData("In Visual Basic, we visualize bugs."); } } |
package com.javapatterns.observer.watching; import java.util.Observable; public class Watched extends Observable { private String data = ""; public String retrieveData() { return data; } public void changeData(String data) { if ( !this.data.equals( data) ) { this.data = data; setChanged(); } notifyObservers(); } } |
package com.javapatterns.observer.watching; import java.util.Observable; import java.util.Observer; public class Watcher implements Observer { public Watcher(Watched w) { w.addObserver(this); } public void update( Observable ob, Object arg) { System.out.println("Data has been changed to: '" + ((Watched)ob).retrieveData() + "'"); } } |
watched.changeData("In C, we create bugs."); watched.changeData("In Java, we inherit bugs."); watched.changeData("In Java, we inherit bugs."); watched.changeData("In Visual Basic, we visualize bugs."); |
Data has been changed to: 'In C, we create bugs.' Data has been changed to: 'In Java, we inherit bugs.' Data has been changed to: 'In Visual Basic, we visualize bugs.' |
![]() 图十、菩萨和菩萨的守瓶乌龟。 |
菩萨做为被观察者对象,继承自Observable类;而守瓶乌龟做为观察者,继承自Observer接口;这个模拟系统的实现能够采用Java对观察者模式的支持达成。设计
Java中的DEM事件机制
AWT中的DEM机制
责任链模式一章中曾谈到,AWT1.0的事件处理的模型是基于责任链的。这种模型不适用于复杂的系统,所以在AWT1.1版本及之后的各个版本中,事件处理模型均为基于观察者模式的委派事件模型(Delegation Event Model或DEM)。
在DEM模型里面,主题(Subject)角色负责发布(publish)事件,而观察者角色向特定的主题订阅(subscribe)它所感兴趣的事件。当一个具体主题产生一个事件时,它就会通知全部感兴趣的订阅者。
使用这种发布-订阅机制的基本设计目标,是提供一种将发布者与订阅者松散地耦合在一块儿的联系形式,以及一种可以动态地登记、取消向一个发布者的订阅请求的办法。显然,实现这一构思的技巧,是设计抽象接口,并把抽象层和具体层分开。这在观察者模式里能够清楚地看到。
使用DEM的用词,发布者叫作事件源(event source),而订阅者叫作事件聆听者(event listener)。在Java里面,事件由类表明,事件的发布是经过同步地调用成员方法作到的。
Servlet技术中的的DEM机制
AWT中所使用的DEM事件模型实际上被应用到了全部的Java事件机制上。Servlet技术中的事件处理机制一样也是使用的DEM模型。
SAX2技术中的DEM机制
DEM事件模型也被应用到了SAX2的事件处理机制上。
观察者模式的效果
观察者模式的效果有如下的优势:
第1、观察者模式在被观察者和观察者之间创建一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
因为被观察者和观察者没有紧密地耦合在一块儿,所以它们能够属于不一样的抽象化层次。若是被观察者和观察者都被扔到一块儿,那么这个对象必然跨越抽象化和具体化层次。
第2、观察者模式支持广播通信。被观察者会向全部的登记过的观察者发出通知,
观察者模式有下面的缺点:
第1、若是一个被观察者对象有不少的直接和间接的观察者的话,将全部的观察者都通知到会花费不少时间。
第2、若是在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,致使系统崩溃。在使用观察者模式是要特别注意这一点。
第3、若是对观察者的通知是经过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
第4、虽然观察者模式能够随时使观察者知道所观察的对象发生了变化,可是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
观察者模式与其它模式的关系
观察者模式使用了备忘录模式(Memento Pattern)暂时将观察者对象存储在被观察者对象里面。
问答题
第一题、我和妹妹跟妈妈说:“妈妈,我和妹妹在院子里玩;饭作好了叫咱们一声。”请问这是什么模式?可否给出类图说明?
问答题答案
第一题答案、这是观察者模式。我和妹妹让妈妈告诉咱们饭作好了,这样咱们就能够来吃饭了。换用较为技术化的语言来讲,当系统的主题(饭)发生变化时,就告诉系统的其它部份(观察者们,也就是妈妈、我和妹妹),使其能够调整内部状态(有开始吃饭的准备),并采起相应的行动(吃饭)。
系统的类图说明以下。
code
![]() |