观察者模式概念
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
这种设计模式在很多的中间价中都有具体的实现,比如在Redis的主从服务器中,当从服务器完成了各种步骤进入与主服务器命令传播的步骤之后,每次主服务器接收到一次外部命令写入,都会把该命令广播给所有的从服务器。这种一对多的关系就是观察者模式,其中观察者是各个从服务器,被观察者是主服务器。下面我们就通过java来实现观察者模式
Java实现观察者模式
我们需要建立一个被观察者以及多个观察者来实现观察者模式,首先是对应被观察者的类,为了让类更明确,我们把被观察者类名设置为PrimaryServer
被观察者:
在被观察者中,我们需要让该类继承Observable类,然后让它具有一个产生消息的方法,在该方法中,首先需要setChanged,把changed位置为true。然后使用notifyObservers()把消息广播给所有观察者,其中notifyObservers()的参数就是要传播的消息
//主服务器 对应为被观察者
public class PrimaryServer extends Observable {
private String servername;
public String getServername() {
return servername;
}
public PrimaryServer(String servername) {
this.servername = servername;
}
public void Produce(Message message){
System.out.println(servername+"produce a message:"+message.getMsg());
setChanged();
notifyObservers(message);
}
}
观察者:
观察者我们需要让它实现Observer接口,并实现update方法,这个方法
是观察者的核心方法。
public class SlaveServer implements Observer {
private String servername;
public String getServername() {
return servername;
}
public SlaveServer(String servername) {
this.servername = servername;
}
@Override
//arg是被观察者调用notifyObservers时传播的信息,o是被观察者
public void update(Observable o, Object arg) {
PrimaryServer primaryServer = (PrimaryServer)o;
Message mes = (Message)arg;
System.out.println("Slave "+servername+" receive a message from " +primaryServer.getServername()+" :"+mes.getMsg());
}
}
我们还需要编辑一个测试类,测试它的方法
public class test {
public static void main(String[] args) {
PrimaryServer primaryServer = new PrimaryServer("主服务器");
SlaveServer s1 = new SlaveServer("从服务器1");
SlaveServer s2 = new SlaveServer("从服务器2");
SlaveServer s3 = new SlaveServer("从服务器3");
primaryServer.addObserver(s1);
primaryServer.addObserver(s2);
primaryServer.addObserver(s3);
primaryServer.Produce(new Message("SET UP 1"));
}
}
得到的结果如下:
主服务器produce a message:SET UP 1
Slave 从服务器3 receive a message from 主服务器 :SET UP 1
Slave 从服务器2 receive a message from 主服务器 :SET UP 1
Slave 从服务器1 receive a message from 主服务器 :SET UP 1
这几个类对应的UML图关系如下:
Observable源码解析
我们进入Observable类查看它的成员和函数:
首先它具有两个成员,分别是 changed标志位,初始设置位false。第二个成员是Vector容器,这是一个利用了很多synchronized修饰的并发安全容器
private boolean changed = false;
private Vector<Observer> obs;
我们来看几个关键的方法
第一个是我们应用的addObserver方法,它就是把传入的观察者参数添加到并发Vector容器中
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
第二个是我们应用的setChanged方法,它其实就是直接把changed的标志位置为true
protected synchronized void setChanged() {
changed = true;
}
第三个就是广播的方法,它的逻辑是创建一个Object数组,并进入synchronized修饰的代码块,把Vector数组的所有内容拷贝到Object数组。然后把标志位设置为false,再调用每一个Observer的update方法
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);
}
我们再回到Obser接口,可以看到它对应有一个方法,就是update方法
,所以我们重载update方法,并利用它的参数就是实现我们自己的逻辑
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}