定义:观察者模式(也称发布者/订阅者模式),提供了对象之间的一种依赖关系,当被依赖者发生改变时,它的所有依赖者都会收到通知,并作出反应。
jdk的util包提供了一个类Observable和一个接口Observer。
Observable(被观察者/发布者):
这个类代表一个可观察到的对象,或数据。它可以被细分为代表一个应用程序想要观察的对象。
一个可观察的对象可以有一个或多个观察者。一个观察者可能是任何实现观察者接口的对象。当一个被观察者发生变化后,应用程序可以调用notifyObservers方法,调用所有观察者的update方法让它们注意到这些变化。
被观察者使用了Vector容器,所以默认按照观察者的添加顺序保存,通知的时候按照相反顺序执行。但是,子类可以改变这些顺序,所以,顺序不保证。
被观察者创建时,它的观察者列表是空集合。两个观察者相等的条件是执行equals时返回true。
public class Observable {
private boolean changed = false;
// 底层使用数组,并且线程安全
private Vector obs;
/**
* 构造一个没有观察者的可观察者
*/
public Observable() {
obs = new Vector<>();
}
/**
* 增加一个观察者,如果已存在,则什么都不做。
* 如果o为null,抛出空指针异常。
* @param o 将被添加的观察者
*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* 从观察者集合中删除一个观察者。
* 如果o为null,无影响
* @param o 将被删除的观察者
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/**
* 通知所有观察者,相当于notifyObservers(null)
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* 通知所有观察者
* @param o 可作为观察者update方法的参数
*/
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();
}
/**
* 标记当前被观察者已经改变,调用hasChanged方法将返回true
*/
protected synchronized void setChanged() {
changed = true;
}
/**
* 将当前被观察者标记为没有改变,调用hasChanged方法将返回false,执行notifyObservers时该方法会被自动调用
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* 获取当前被观察者是否已经改变
* @return true or false
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* 返回观察者数量
* @return 观察者数量
*/
public synchronized int countObservers() {
return obs.size();
}
}
Observer(观察者/订阅者):
当想要收到可观察对象变化的通知时,类可以实现Observers接口。该接口只有一个update方法,每当观察到的对象被更改时,都会调用此方法。一个应用程序调用一个可观察到的对象的“notifyObservers”方法,让这个对象的所有观察者都知道这个变化。
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an Observable object's
* notifyObservers
method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the notifyObservers
* method.
*/
void update(Observable o, Object arg);
}
1、定义自己的被观察者对象:继承Observable类,然后按需要扩展字段和方法(引起对象变化的接口)。
例如:微信公众号提供了最新文章的字段,和发布文章的方法。
public class WeChatSubscription extends Observable {
private String newest;
/**
* 发布文章
* @param title 文章标题
*/
public void publish(String title) {
System.out.println("微信公众号发布了文章:" + title);
this.newest = title;
// 设置改变
setChanged();
// 提醒观察者
notifyObservers(title);
}
/**
* 提供数据的获取接口,以便观察者主动拉数据
* @return
*/
public String getNewest() {
return newest;
}
}
2、创建观察者对象:实现Observer接口,重写update方法。可按需扩展字段和方法。
例如:订阅者增加了被观察者的引用,以获得更加灵活的用法。
public class Subscriber implements Observer {
Observable observable;
/**
* 构造器,设置被观察者,并将自己设为观察者
* @param observable 被观察者
*/
public Subscriber(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
/**
* 当被观察者改变时,主动调用
*/
@Override
public void update(Observable o, Object arg) {
System.out.println("订阅者收到新文章:" + arg);
}
/**
* 用于主动从被观察者拉数据
* @return 最新文章标题
*/
public String get() {
if (observable instanceof WeChatSubscription) {
WeChatSubscription weChatSubscription = (WeChatSubscription) observable;
return weChatSubscription.getNewest();
}
throw new UnsupportedOperationException("不支持的操作!");
}
}
3、写测试。
public class Test {
public static void main(String[] args) {
// 创建可观察者:这里是微信公众号
WeChatSubscription observable = new WeChatSubscription();
// 创建观察者:这里是微信用户
Subscriber observer = new Subscriber(observable);
// 可观察者发生改变:发布新文章
observable.publish("观察者模式");
// 观察者自己拉取最新文章
String title = observer.get();
System.out.println("观察者自己拉取最新文章:" + title);
}
}
4、结果。
微信公众号发布了文章:观察者模式
订阅者收到新文章:观察者模式
观察者自己拉取最新文章:观察者模式
JDK提供了一套观察者模式,该工具的优点是:拥有比较完整的接口,继承或者实现之后无需过多处理就可以使用,而且可以实现观察者自己手动拉数据的功能。但是也存在确点,Observable是一个类,是“类”,所以,失去了灵活性。