观察者模式(Observer)定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象,当这个主题对象发生变化的时候,会通知所有的观察者对象,让他们主动更新自己。
还是举个形象的例子:大学英语课的时候上听力课,一般在语音教室里面,每个人位子上都会有个耳机,这个耳机的通路都是老师可以控制的,老师先把所有的通路都打开(增加观察者)。学生都戴着耳机听着老师的口令做出对应的动作,老师让跟读就跟读(发出通知)。这里老师就可以当做是被观察者,学生(N个)就是观察者,这里一个很明显的一对多关系。当老师把某几个学生的通路切断之后,这些学生是听不到老师的口令,也就是说失去了对老师的观察(主题角色删除观察者角色),当老师再发出口令的时候他们就不会做出对应的反馈,因为收不到“通知”. 这个例子就是个非常形象的观察者模式原型,技术源于生活:-)
观察者模式的组成:
-->抽象主题角色:把所有对观察者的引用保存在一个集合里面,每个抽象主题角色可以有任意数量的观察者,抽象主题提供一个接口,这个接口可以用来增加和删除观察者,一般用一个抽象类或者接口实现。
-->抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的时候更新自己。
-->具体主题角色:在主题角色内部内容发生改变的时候,给所有登记过的观察者发出通知,触发观察者做出相应的改变。
-->具体观察者角色:实现抽象观察者角色的接口内容,以便在收到主题角色的通知的时候做出相应的改变,从而达到与主体角色的协调,如果需要可以保存一个对主题角色的引用,通常用一个子类表示。
下面我们来实现一个自己的观察者模式:
主要就是按照上面四部分+一个测试客户端组成:
抽象主题角色:Watched.java
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.java.obsevabelTest1;
public interface Watched {
//这里的几个函数都是观察者模式所需要具备的
public void addWatcher(Watcher w);//增加观察者
public void removeWatcher(Watcher w);//删除观察者
public void notifyWatchers(String str);//发出通知,这里的str是作为通知的参数传递出去的。
}
</span>
抽象观察者角色:Watcher.java
<span style="font-family:Microsoft YaHei;">package com.java.obsevabelTest1;
public interface Watcher {
public void update(String str);//观察者收到通知更新消息
}</span>
具体主题角色:ConcerateWatched.java
<span style="font-family:Microsoft YaHei;">package com.java.obsevabelTest1;
import java.util.ArrayList;
import java.util.List;
public class ConcerateWatched implements Watched {
List<Watcher> listWatcher = new ArrayList<Watcher>();//list结构,用于保存所有的观察者
@Override
public void addWatcher(Watcher w) {
listWatcher.add(w);
}
@Override
public void removeWatcher(Watcher w) {
listWatcher.remove(w);
}
/**
* 这里的通知所有的观察者实际上是一个遍历list所有对象,对对象的update方法进行调用而已。
*/
@Override
public void notifyWatchers(String str) {
for(Watcher ws:listWatcher){
ws.update(str);
}
}
}</span>
具体观察者角色:ConcerateWatcher.java
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.java.obsevabelTest1;
public class ConcerateWatcher implements Watcher {
@Override
public void update(String str) {
System.out.println("get Message: "+ str);//输出更新后的消息
}
}
</span>
测试客户端:TestMain.java
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.java.obsevabelTest1;
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
Watched cWatched =new ConcerateWatched();
Watcher w1 = new ConcerateWatcher();
Watcher w2 = new ConcerateWatcher();
Watcher w3 = new ConcerateWatcher();
cWatched.addWatcher(w1);
cWatched.addWatcher(w2);
cWatched.addWatcher(w3);
cWatched.notifyWatchers("被观察者更新了!");
cWatched.removeWatcher(w2);
cWatched.notifyWatchers("移除一个又更新了!");
}
}
</span>
输出结果:
get Message: 被观察者更新了!
get Message: 被观察者更新了!
get Message: 被观察者更新了!
get Message: 移除一个又更新了!
get Message: 移除一个又更新了!
上面的例子其实就是观察者模式的全部,相对来说还是比较简单的,观察者模式在实际运用中比较多,很重要,所以java对观察者模式在API中也提供了对应的接口。
Observabel类和Observer接口,其中Observabel就是被观察者,而接口Observer就是观察者接口。当深入到API中去查看源码的时候其实就会发现,这个类和接口的实现和我们上面的demo差不多的。不过这里有个差异就是当被观察者有变更的时候需要发出setchange的动作才能触发观察者“收到”通知。
例子:
这里采用java API中现成的类实现相应的观察者操作,定义了两个观察者当计数器达到5之后,将第二个观察者踢出去。
被观察者类:
ObservalCon.java
<span style="font-family:Microsoft YaHei;">package com.java.obsevabelTest1;
import java.util.Observable;
public class ObservalCon extends Observable {
/**
* 这里封装的原因是setChanged是protect类型,继承类是直接调用不了的,内部封装下即可。其实也可以直接在main测试的那个
* 类中直接继承Observable的类。这样就可以直接this.setChanged()调用了。
*/
public void setChangeValue(){
this.setChanged();
}
}</span>
观察者类1.
ObservalClient.java
<span style="font-family:Microsoft YaHei;">package com.java.obsevabelTest1;
import java.util.Observable;
import java.util.Observer;
public class ObservalClient implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("obseral 1 get msg is :"+arg);
}
}</span>
观察者类2.
ObservalClient2.java
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.java.obsevabelTest1;
import java.util.Observable;
import java.util.Observer;
public class ObservalClient2 implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("obseral 2 get msg is :"+arg);
}
}
</span>
测试客户端.
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.java.obsevabelTest1;
import java.util.Observable;
public class TwoObservalMain {
/**
* @param args
*/
public static void main(String[] args) {
ObservalCon obCon = new ObservalCon();
//Observable obCon = new Observable();
ObservalClient obClient1 = new ObservalClient();
ObservalClient2 obClient2 = new ObservalClient2();
obCon.addObserver(obClient1);
obCon.addObserver(obClient2);
for(int i=0;i<10;i++){
obCon.setChangeValue();
obCon.notifyObservers(i);
if(i>=5){
obCon.deleteObserver(obClient2);
}
}
}
}</span><span style="font-size:14px;">
</span>
输出结果如下:
obseral 2 get msg is :0
obseral 1 get msg is :0
obseral 2 get msg is :1
obseral 1 get msg is :1
obseral 2 get msg is :2
obseral 1 get msg is :2
obseral 2 get msg is :3
obseral 1 get msg is :3
obseral 2 get msg is :4
obseral 1 get msg is :4
obseral 2 get msg is :5
obseral 1 get msg is :5
obseral 1 get msg is :6
obseral 1 get msg is :7
obseral 1 get msg is :8
obseral 1 get msg is :9
可以看到,从5输出之后,就只有1才能收到通知了,这里知道为啥是后从obseral2 然后再obseral 1呢?看源码去探索下:-)