一、简单介绍
(1)观察者模式:在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。简单的理解为“发布---订阅”。
(2)应用实例:
1、京东上某个商品暂时没货,提示用户关注后到货通知,这个暂时无货的商品是被观察者,点击关注这个商品的用户就是观察者。
2、老师针对成绩在60分以下的同学定期发送最新的考题分析邮件,每轮考试下来都会有不及格的同学,由不及格变为及格的同学自动从邮件列表里移除,新的不及格的同学会被加进邮件列表里。
(3)优点:
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
(4)缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
(5)使用场景:
1、有多个子类共有的方法,且逻辑相同。
2、重要的、复杂的方法,可以考虑作为模板方法。
(6)注意事项:
1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式后面介绍Rxjava。
二、源码分析
(1)观察者:
public interface Observer {
void update(Observable o, Object arg);
}
太简单了,就是一个接口包含一个update方法。
(2)被观察者:
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) {
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();
}
}
也很简单,方法简单明了。内部使用Vector容器保证线程安全,另外基本上每个方法都是synchronized修饰,用来实现同步锁。唯独有一个方法值得一提:
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);
}
此方法并没有进行用synchronized修饰,原因很简单:A线程执行通知并且观察者update的时候,如果B线程也执行通知并且update,如果update是耗时的操作,那么A、B 线程会相互阻塞。所以只是内部使用了同步锁,锁住了更新状态和list的副本获取,保证数据同步。
三、实例使用
(1)我们自己的观察者
public class Watcher implements Observer {
@Override
public void update(Observable o, Object arg) {
try {
Thread.sleep(5000);//模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(o.toString()+" "+arg.toString());
}
}
(2)我们自己的被观察者
public class BeWatcher extends Observable{
public void TestChange(int count){
setChanged();
notifyObservers(count);
}
}
(3)测试类
public class TestObserverMain {
public static void main(String[] args){
BeWatcher beWatcher = new BeWatcher();
Watcher watcher = new Watcher();
beWatcher.addObserver(watcher);
for(int i=0;i<2;i++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
beWatcher.TestChange((int)Thread.currentThread().getId());
}
});
t.start();
}
}
}