什么是观察者模式
用报纸订阅的方式来说明:
1、报社的业务就是出版报纸
2、向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,就会一直收到报纸
3、当你不想再看报纸的时候,取消订阅,他们就不会再送来新报纸
4、只要报社还在运营,就会一直有人向他们订阅报纸或者取消订阅报纸
观察者模式的流程跟报纸订阅方式一致,即:观察者模式=出版者+订阅者,只是名称不一样,出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observer)。
参与角色
观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
主题接口(Subject):对象使用此接口注册为观察者,或者把自己从观察者中删除。
具体主题(ConcreteSubject):一个具体主题总是实现主题接口,除了注册和撤销方法以外,具体主题还实现了notifyObserves()方法,此方法用于在状态改变时更新所有当前观察者。
观察者(Observer):为那些在目标发生改变时需获得通知的对象定义更新的接口
具体观察者(ConcreteObserver):具体观察者实现了观察者Observer接口,并注册具体主题,以便接收更新。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合,当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节,主题只知道观察者实现了Observer接口,不需要知道观察者的具体类是谁,做了些什么其他的细节。
实例
通过ConcreteObserver注册两个观察者ConcreteObserverA和ConcreteObserverB,一旦被观察者有更新,两个观察者会立刻收到更新通知。具体代码如下:
声明Subject主题接口,Subject.java:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
声明ConcreteObserver具体主题,NetConcreteSubject.java:
public class NetConcreteSubject implements Subject {
private List<Observer> observers;
private ObserverBean bean;
public NetConcreteSubject() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int pos = observers.indexOf(observer);
if (pos > 0) {
observers.remove(pos);
}
}
@Override
public void notifyObservers() {
if (bean == null) return;
for (int i = 0; i < observers.size(); i++) {
Observer observer = observers.get(i);
observer.update(bean);
}
}
public void setStatusChanged(ObserverBean bean) {
this.bean = bean;
notifyObservers();
}
}
声明观察者接口, Observer.java:
public interface Observer {
void update(Object obj);
}
声明具体观察者类,ConcreteObserverA.java:
public class ConcreteObserverA implements Observer {
@Override
public void update(Object obj) {
if (obj instanceof ObserverBean) {
ObserverBean bean = (ObserverBean) obj;
System.out.print("ConcreteObserverA 网络类型:"
+ bean.netType + ",网络状态:" + bean.netStatus + "\n");
}
}
}
ConcreteObserverB.java:
public class ConcreteObserverB implements Observer {
@Override
public void update(Object obj) {
if (obj instanceof ObserverBean) {
ObserverBean bean = (ObserverBean) obj;
System.out.print("ConcreteObserverB 网络类型:"
+ bean.netType + ",网络状态:" + bean.netStatus + "\n");
}
}
}
调用过程:
public static void main(String[] args) {
//初始化主题(被观察者)
NetConcreteSubject subject = new NetConcreteSubject();
//初始化观察者A
ConcreteObserverA observerA = new ConcreteObserverA();
//初始化观察者B
ConcreteObserverB observerB = new ConcreteObserverB();
//注册观察者A
subject.registerObserver(observerA);
//注册观察者B
subject.registerObserver(observerB);
//通知所有观察者数据有更新
ObserverBean bean = new ObserverBean("wifi", "有网");
subject.setStatusChanged(bean);
}
运行结果:
ConcreteObserverA 网络类型:wifi,网络状态:有网
ConcreteObserverB 网络类型:wifi,网络状态:有网
系统内置观察者
Observable.java:
public class Observable {
private boolean changed = false;
private Vector<Observer> obs = new Vector();
public Observable() {
}
public synchronized void addObserver(Observer var1) {
if(var1 == null) {
throw new NullPointerException();
} else {
if(!this.obs.contains(var1)) {
this.obs.addElement(var1);
}
}
}
public synchronized void deleteObserver(Observer var1) {
this.obs.removeElement(var1);
}
public void notifyObservers() {
this.notifyObservers((Object)null);
}
public void notifyObservers(Object var1) {
Object[] var2;
synchronized(this) {
if(!this.changed) {
return;
}
var2 = this.obs.toArray();
this.clearChanged();
}
for(int var3 = var2.length - 1; var3 >= 0; --var3) {
((Observer)var2[var3]).update(this, var1);
}
}
public synchronized void deleteObservers() {
this.obs.removeAllElements();
}
protected synchronized void setChanged() {
this.changed = true;
}
protected synchronized void clearChanged() {
this.changed = false;
}
public synchronized boolean hasChanged() {
return this.changed;
}
public synchronized int countObservers() {
return this.obs.size();
}
}
Observer.java:
public interface Observer {
void update(Observable var1, Object var2);
}
上面的Observer和Observable类都位于java.util目录中,是JDK中已经写好的,那么我们可以直接拿来用了:
主题类NetSubject.java:
public class NetSubject extends Observable {
public void setNetChanged(ObserverBean bean){
setChanged();
notifyObservers(bean);
}
}
观察者SystemObserverA.java:
public class SystemObserverA implements Observer {
@Override
public void update(Observable observable, Object o) {
if (o instanceof ObserverBean
&& observable instanceof NetSubject) {
ObserverBean bean = (ObserverBean) o;
System.out.print("ConcreteObserverA 网络类型:"
+ bean.netType + ",网络状态:" + bean.netStatus + "\n");
}
}
}
观察者SystemObserverB.java:
public class SystemObserverB implements Observer {
@Override
public void update(Observable observable, Object o) {
if (o instanceof ObserverBean
&& observable instanceof NetSubject) {
ObserverBean bean = (ObserverBean) o;
System.out.print("ConcreteObserverB 网络类型:"
+ bean.netType + ",网络状态:" + bean.netStatus + "\n");
}
}
}
执行程序:
//初始化主题(被观察者)
NetSubject subject = new NetSubject();
//初始化观察者A
SystemObserverA observerA = new SystemObserverA();
//初始化观察者B
SystemObserverB observerB = new SystemObserverB();
//注册观察者A
subject.addObserver(observerA);
//注册观察者B
subject.addObserver(observerB);
//通知观察者有更新
ObserverBean bean = new ObserverBean("4G流量", "有网");
subject.setNetChanged(bean);
运行结果:
ConcreteObserverB 网络类型:4G流量,网络状态:有网
ConcreteObserverA 网络类型:4G流量,网络状态:有网
注意:
当使用JDK内置观察者模式时,当需要通知观察者更新数据时,首先需要调用setChanged()来改变状态,否则观察者不会收到任何更新通知。为什么会这样呢,来看下JDK中的源码就知道了:
private boolean changed = false;
protected synchronized void setChanged() {
this.changed = true;
}
public void notifyObservers(Object var1) {
Object[] var2;
synchronized(this) {
//如果changed为false,将直接返回
if(!this.changed) {
return;
}
var2 = this.obs.toArray();
this.clearChanged();
}
for(int var3 = var2.length - 1; var3 >= 0; --var3) {
((Observer)var2[var3]).update(this, var1);
}
}
通过setChanged()将changed设置为true,否则当调用notifyObservers(Object var1)将没有任何作用。
使用JDK内置观察者模式看似程序更简单了,但缺点也暴露出来了:
1、Observable是一个类,而不是一个接口,导致Observable类的扩展性不高,限制了它的使用和复用。
2、Observable将某些方法保护了起来(setChanged()和clearChanged()为protected),这意味着除非继承自Observable,否则将有关键的方法不能调用。导致无法通过组合的方式使其它类获得Observable类的功能
所以,如果JDK内置的观察者模式符合你的需求,那么可以尽情的使用内置的观察者模式,否则应该自己实现一套观察者模式。