观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统,订阅—发布系统。另外Android中的listview中的notifyDataSetChange函数,都是使用观察者模式。另外Android中的回调模式也有点观察者模式的影子,很像,但并不是完全的观察者模式,回调模式是一对一,观察者模式是一对多。
观察者模式的一个重要作用是解耦,将观察者与被观察者解耦,使得他们之间依赖性更小,甚至做到毫无依赖。
正如上面所说,观察者模式是一种一对多的依赖关系,使得一个对象改变状态时,则所有依赖它的对象都会得到通知并被自动更新。
观察者模式的框图如下:
Subject:抽象主题角色:也就是被观察者(Observer)的角色。把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类或接口来实现。
ConcreteSubject:具体主题,也叫具体被观察者,该角色将有关状态存入具体的观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知。
Observer:抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
ConcreteObserver:具体的观察者,该角色实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。
观察者模式的简单实现:
这里模拟一个网站发布新消息的情形。
UML类图
首先定义Website.java,作为ConcreteSubject。
package com.test.observable;
/**
* Website网站用来作为被观察者
* @author QHF
*
*/
import java.util.Observable;
public class Website extends Observable{
public void postNews(String content) {
setChanged();
notifyObservers(content);
}
}
其中Observable是Subject角色,系统已经帮我们实现,源码如下:已删除注释,并添加自己的注释
public class Observable {
//标志位 在通知所有观察者们的时候需要用到
private boolean changed = false;
//存放观察者的集合
private Vector 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();
}
//设置标志位为true
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();
}
}
然后是Reader.java,表示ConcreteObserver角色
package com.test.observable;
/**
* 网站阅读者 作为观察者
*/
import java.util.Observable;
import java.util.Observer;
public class Reader implements Observer{
public String name;
public Reader(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
System.out.println("Hi, " + name + ",网站更新啦,内容: " + arg);
}
}
Observer抽象类表示Observer角色,系统已经实现。源码如下
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an Observable object's
* notifyObservers
method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the notifyObservers
* method.
*/
void update(Observable o, Object arg);
}
然后是客户端Client.java
package com.test.observable;
public class Client {
public static void main(String[] args) {
//被观察者
Website website = new Website();
//观察者
Reader zhangsan = new Reader("张三");
Reader lisi = new Reader("李四");
Reader wangwu = new Reader("王五");
//将观察者注册到可观察者的观察列表中
website.addObserver(zhangsan);
website.addObserver(lisi);
website.addObserver(wangwu);
//发布消息
website.postNews("新的一期网站周报发布啦!!!!,地址为www.uestc.edu.cn");
}
}
运行之后出结构为
Hi, 王五,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn
Hi, 李四,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn
Hi, 张三,网站更新啦,内容: 新的一期网站周报发布啦!!!!,地址为www.uestc,edu.cn
可以看到所有订阅了网站周报的人都收到了更新消息,这就是一个简单的订阅发布系统。在这个过程中,Reader和Website都是依赖于Observer和Observable这些抽象类,因此对于Reader和Website完全没有耦合,保证了订阅系统的灵活性和可扩展性。