本章继续设计模式中的观察者模式的学习。本章将以问答的形式给出。
1. 什么是观察者模式?
观察者模式定义了对象之间一对多的关系,当一个对象发生变化时,所有其他对象都会收到该对象变化的消息。
2.能举个典型的例子吗?
我们都对马云的围脖感兴趣,然后我们都加他为好友。每次他更新围脖,我们都会收到他新的消息。其实可以把马云看作一个主题,我们都是订阅了主题的观察者,每当主题变化,我们都能收到最新的消息。这就是观察者模式。
3.那代码怎么写?
我先贴一段,大家看效果,然后再说内部如何实现.
import java.util.Observer;
/**
* 这里是围脖
* 用户小明关注了JackMsgSubject的变化
*/
public class Weibo {
public static void main(String[] args) {
//马云的围脖
JackMsgSubject jackMsg = new JackMsgSubject();
//小明关注了马云
Observer xiaoming = new XiaoMing(jackMsg);
//马云发了一条新的消息
jackMsg.putNewMsg("阿里巴巴是个快乐的青年!");
//然后
}
}
主题部分代码,主题是数据的承载者,当主题变化时,所有订阅者都会收到消息:
import java.util.Observable;
/**
* 主题,变化的对象,包含变化的数据
* Observable是一类 ,而不是接口!!!
* JackMsgSubject 是继承自Observable 而不是implements
*/
public class JackMsgSubject extends Observable {
private String aMsg;
public void putNewMsg(String newMsg){
//发了一个新围脖
this.aMsg = newMsg;
//aMsg已经发生变化
this.setChanged();
//通知所有关注我人
this.notifyObservers();
}
public String getNewMsg(){
return aMsg;
}
}
看下小明,他关注了主题JackMsg,当JackMsg发生变化时,小明会收到JackMsg变化的消息内容。观察者的实现:
import java.util.Observable;
import java.util.Observer;
/**
* 小明 是一个观察者
* 他关注了jack 的围脖JackMsgSubject变化
*/
public class XiaoMing implements Observer {
//我关注了某人
private Observable abs;
public XiaoMing(Observable abs){
//小明关注了abs
this.abs=abs;
//将自己加入abs队列中 以监听其变化
this.abs.addObserver(this);
}
public void update(Observable o, Object arg) {
if(o instanceof JackMsgSubject){
JackMsgSubject jackMsg = (JackMsgSubject) o;
System.out.print("我收到了Jack的消息:"+jackMsg.getNewMsg());
}
}
}
当执行main()函数的时候,我们就会在console打印出:
4.观察者如何实现的?
我们这里用的是jdk自带的Observable类/observer接口。其中Observable为主题,包含变化的数据,很诡异的Observable竟然是一个类,而不是接口!!我们看看Observable里面干了什么:
public class Observable {
private boolean changed = false;
private Vector obs; //此处存储的是所有观察者名单 相当于注册列表
//****省略其他方法
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
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); //调用所有观察者Observer共同实现的接口 update()
}
大家看代码就可以发现,对于主题来说,维护了一个观察者的Vector,当有新的观察者想要关注该主题是,只需要将其加入Vector,即调用vector.add()方法。当变化发生的时候,取出Vecotor中每个观察者,逐一条用Observer接口中的update方法即可。
5. 观察者中的推/拉模式
我个人理解,新浪围脖是个大的观察者网络。每个人既可以是主题,也可以是观察者。开始的时候,消息是推送的,什么意思呢?就是有100个人关注了我,那么我发一条新围脖,这条信息会推送到100个人的页面上,不管这个用户是否在线,也不管后面的消息是否会被更新的覆盖,反正就是推过去了。这样看起来好简单,但是问题也是很明显的。就是消息发过去了,用户也未必会关注,或者至少不会立刻关注,这就造成了网络资源的浪费,而且使得在线用户收到消息速度变慢。
那可以部分采用“拉”的模式,在线用户我推送,不在线用户,等下次上线再去把这个消息“拉”回来,既保证在线用户收消息速度,又保证所有人都可以收到这条消息不丢失。
如何实现“拉”的方式?其实我们这里就是“拉”模式。因为主题JackMsgSuject中实现了 getNewMsg()方法,这样观察者在收到新消息通知时,可以选择自己去拉出来,或者延迟去取。
好了,初步的观察者模式就到这里了。对于业务逻辑比较复杂的观察者模式,最好是自己实现一套机制,因为JDK中主题Obserable是个类!而不是接口!所以涉及到继承等操作的时候,灵活性会大大降低!