观察者模式
前言
最近在看《深入浅出Nodejs》,看完了第三章异步IO,觉得观察者模式在Node中十分重要于是找了几篇博客学习了一下观察者模式。
主要的参考博客:
《JAVA与模式》之观察者模式——java_my_life
java:从消息机制谈到观察者模式——luoweifu
观察者模式的定义
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式结构
偷懒直接拿了别人的图:
- Subject 抽象主题,也被叫做被观察者(Observable),抽象主题提供一个接口,可以添加和删除观察者
- ConcreteSubject 具体主题,也被叫做具体被观察者(Concreate Obervable),存放有关状态,并且在状态改变时通知观察者
- Observer 抽象观察者,为所有具体观察者定义一个更新接口,用于在得到主题更新时更新自己,并做一些相应操作。
- ConcreteObserver 具体观察者,存储与主题相关的状态并且实现更新接口,可以持有一个具体主题对象的引用。
推模型和拉模型
观察者模式可以分为拉模型和推模型两种:
- 推模型 主题对象像观察者推送信息,不管观察者是否需要,推送的信息一般是主题对象的全部或部分数据。
- 拉模型 主题对象在通知观察者时,只提供少量信息,若观察者需要更多信息,则直接到主题对象中获取数据。这样就需要通过update()方法将主题对象传递给观察者,或者让观察者持有一个具体主题对象的引用。
推模型
//Observer.java
public interface Observer {
void update(String newState);
}
//Observable.java
public abstract class Observable {
private ArrayList<Observer> observerList = new ArrayList<>();
public void addObserver(Observer o) {
observerList.add(o);
}
public void removeObserver(Observer o) {
observerList.remove(o);
}
public void nodifyObservers(String newState) {
for(Observer observer : observerList) {
observer.update(newState);
}
}
}
//ConcreteObserver.java
public class ConcreteObserver implements Observer {
public ConcreteObserver(Observable o) {
o.addObserver(this);
}
@Override
public void update(String newState) {
System.out.println("状态已更新:" + newState);
}
}
//ConcreteObservable.java
public class ConcreteObservable extends Observable {
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
if(!this.state.equals(state)) {
this.state = state;
this.nodifyObservers(state);
}
}
}
//Client.java
public class Client {
public static void main(String[] args) {
ConcreteObservable concreteObservable = new ConcreteObservable();
ConcreteObserver concreteObserver = new ConcreteObserver(concreteObservable);
concreteObservable.setState("神清气爽");
}
}
/*OUTPUT:
状态已更新:神清气爽
*/
拉模型
Java提供的观察者模式应该就是一种拉模型(个人理解),下面的代码删去了一些源码中的注释。
Java源码
//Observer.java
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}
//Observable.java
package java.util;
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
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);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
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();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
需要注意的是notifyObservers()这个方法中,从Vector中取出Observers并放在一个Object数组中,这个操作是在synchronized块中的。但是调用Observer的update()没有在synchronized块中(猜想这个地方是为了防止Observer的update()也需要锁住Observable而造成死锁,也许也有防止被Observa被锁住太长时间的目的),因此会有竞争的风险
使用
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();
}
this.notifyObservers();
}
}
Watcher.java
public class Watcher implements Observer{
public Watcher(Observable observable) {
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("状态已改变:" + ((Watched)o).getData());
}
}
DemoClient.java
public class DemoClient {
public static void main(String[] args) {
Watched watched = new Watched();
Watcher watcher = new Watcher(watched);
watched.setData("好——像有点不对劲!");
}
}
/*OUTPUT:
状态已改变:好——像有点不对劲!
*/