大部分人每天打开手机都会看到各种APP推送的消息,这个推送是怎么做到的呢?在GUI软件上点击一个按钮,正常情况下都会有一些反应,如何实现的呢?其实这些实现都可以使用观察者模式来实现。
原理
观察者模式的原理其实很简单,其核心主要是:
- 主题
- 观察者
主题即观察者关心的东西,例如在APP推送这个场景中,用户是观察者,主题就是APP,用户在安装APP后并开启消息推送功能表明用户关系该APP的消息,当APP有变化的时候,用户就会收到推送。如下图所示:
关系图实现
实现的关键是明确主题和观察者应该做什么事,主题应该做什么事,主题是会改变的,当发生改变的时候,应该有手段通知到订阅该主题的观察者们,如何做呢?用回调。主题消息改变之后会调用类似notify()的方法,在该方法里调用观察者们的类似update()方法,update()方法是观察者自行实现的,具体处理逻辑完全可以不同。
一般有两种实现方法,一种是自己手动实现,这需要自己从头到尾编写接口,类等。另一种是依赖java.util包下的Observable类和Observer接口,其中Observable类帮我们实现了很多方法,可以直接使用。
自己手动实现
直接上代码即可,有注释:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
public interface Observer {
void update(Object arg);
}
public class NameSubject implements Subject {
private String name;
//存储订阅该主题的观察者
private final List<Observer> observers;
public NameSubject() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(name);
}
}
public void changeName(String name) {
this.name = name;
notifyObservers();
}
}
public class AObserver implements Observer{
@Override
public void update(Object arg) {
System.out.println("A listen arg is : " + arg);
}
}
public class BObserver implements Observer{
@Override
public void update(Object arg) {
System.out.println("B listen arg is : " + arg);
}
}
public class CObserver implements Observer{
@Override
public void update(Object arg) {
System.out.println("C listen arg is : " + arg);
}
}
public class Main {
public static void main(String[] args) {
NameSubject nameSubject = new NameSubject();
Observer aObserver = new AObserver();
Observer bObserver = new BObserver();
Observer cObserver = new CObserver();
//观察者订阅主题
nameSubject.registerObserver(aObserver);
nameSubject.registerObserver(bObserver);
nameSubject.registerObserver(cObserver);
//推送消息
nameSubject.changeName("yeonon");
//观察者取消订阅
nameSubject.removeObserver(aObserver);
nameSubject.changeName("weiyanyu");
}
}
启动测试类,可以看到如下输出:
A listen arg is : yeonon
B listen arg is : yeonon
C listen arg is : yeonon
B listen arg is : weiyanyu
C listen arg is : weiyanyu
基本符合预期。
基于JDK实现
代码如下:
public class NameSubject extends Observable {
private String name;
public void changeName(String name) {
this.name = name;
//调用notifyObservers之前需要调用setChanged方法表示已经改变
setChanged();
notifyObservers(this.name);
}
}
public class AObserver implements Observer {
private Observable observable;
public AObserver(Observable observable) {
this.observable = observable;
this.observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
System.out.println("A listen arg is : " + arg);
}
}
//B和C的代码几乎一样,不再贴上来了.....
public class Main {
public static void main(String[] args) {
NameSubject nameSubject = new NameSubject();
Observer aObserver = new AObserver(nameSubject);
Observer bObserver = new BObserver(nameSubject);
Observer cObserver = new CObserver(nameSubject);
nameSubject.changeName("yeonon");
nameSubject.deleteObserver(aObserver);
nameSubject.changeName("weiyanyu");
}
}
运行测试类,可以看到类似如下输出:
C listen arg is : yeonon
B listen arg is : yeonon
A listen arg is : yeonon
C listen arg is : weiyanyu
B listen arg is : weiyanyu
注意到这里顺序和上面手动实现的不太一样,原因是Observable类内部的实现是使用Vector类实现的,而且在遍历Vector的时候是从后往前遍历的。如下代码所示:
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
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);
}
小结
现在可以给出观察者模式的定义了:观察者模式定义了对象之间的一对多依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
使用观察者模式的例子还有很多,例如Netty里使用类似方式实现事件循环,还有channelPipline里的事件传播等。善用观察者模式可以实现很多有意思的功能!
本系列文章参考书籍是《Head First 设计模式》,文中代码示例出自书中。
网友评论