观察者模式是对象的行为型模式,又叫做发表—订阅模式,模型—试图模式,源收听者模式或者从属者模式。它定义了一对多的依赖关系,让一个或者多个观察者对象订阅一个主题对象,这样一个主题对象在状态上变化就能够通知所有的依赖于此对象的那些观察者对象,使这些观察者能够自动更新。
观察者模式的结构图
从结构图中我们可以看出,观察者存在下面这几种角色:
抽象主题(Subject)角色
可以用一个抽象类或者一个接口来实现,在具体的情况下,也不排除使用具体类实现。抽象观察者(Observer)角色
为所有的具体观察者定义一个接口。在得到通知时更新自己,抽象观察者角色也不排除用使用具体类来实现。具体主题(ConcreateSubject)角色
具体主题保存对具体对象者对象游泳的内部状态,在这种内部状态改变时,给其观察者发出一个通知,具体主题对象又称作具体被观察者角色。具体观察者(ConcreateObserver)角色
具体观察者角色用于保存一个指向具体主题对象的作用,和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口。
java语言提供的对观察者模式的支持
在Java语言的java.util库中,分别提供了Observable类和一个Observer接口。
Observer接口
接口只定义了一个update()方法。当被观察者对象状态发生改变时,update()就会被调用。它会调用每一个被观察者对象的notifyObservers()方法,从而通知所有的观察者对象。
import java.util.Observable;
public interface Observer {
/*
* 当被观察的对象发生变化时,这个方法会被调用
*/
void update(Observable o,Object arg);
}
Observable类
这个方法中有两个子类:setChange(),另一个是notifyObservers(),setChange()方法调用代表状态被改变,notifyObservers()调用则调用所注册过的观察者的update()方法。使这些观察者可以更新自己。
public class Observable {
private boolean changed = false;
private Vector obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector();
}
/**
* 将一个观察者添加到观察者聚集上面
*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* 将一个观察者从观察者聚集上删除
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
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;
}
/**
* Returns the number of observers of this <tt>Observable</tt> object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
怎样使用JAVA对观察者模式的支持
在这个例子中,被观察对象叫做Watched;而观察者对象叫做Watcher。Watched对象继承自java.util.Observable类;而Watcher对象实现了java.util.Observer接口。另外有一个Test类扮演客户端角色。
Watched.java源代码:
public class Watched extends Observable{
private String data = "";
public String getData() {
return data;
}
public void setData(String data) {
if(!this.data.equals(data)){
this.data = data;
setChanged();
}
notifyObservers();
}
}
观察者类源代码
public class Watcher implements Observer{
public Watcher(Observable o){
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("状态发生改变:" + ((Watched)o).getData());
}
}
测试类源代码
public class Test {
public static void main(String[] args) {
//创建被观察者对象
Watched watched = new Watched();
//创建观察者对象,并将被观察者对象登记
Observer watcher = new Watcher(watched);
//给被观察者状态赋值
watched.setData("start");
watched.setData("run");
watched.setData("stop");
}
}
总结:
观察者模式的优点:
- 被观察者将不认识任何一个具体观察者,它只知道他们都有一个共同的接口。
- 观察者模式支持广播通信,被观察者会想所有的登记过观察者发出通知。
观察者模式的缺点:
- 通知所有的观察者耗时长;
- 如果在被观察者之间有循环依赖的话,被观察者会触发他们之间进行循环调用。会导致系统奔溃
- 如果对观察者的通知时通过另的县城进行异步投递的话。系统必须保证投递时一自恰的方式进行的。
- 观察者模式没有响应的机制使观察者知道所观察的对象是怎么发生变化的。