观察者模式主要是用于一对多模块之间通信的解耦.
它的基本UML如下
Subject: 抽象主题,也就是被观察者角色,抽象主题角色把所有观察者对象的引用都保存到一个集合里,每个主题可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象.
ConcreteSubject: 具体主题,该角色将有关状态存入具体,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(Concrete Observable)角色
Observer: 抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得主题更改通知时更新自己
ConcreteObserver: 具体的观察者,该角色实现抽象观察者角色定义的更新接口,以便在主题的状态发生变化时更新自己的状态
下面是一个观察者模式的简单实现:
开发技术前沿网站(www.devtf.cn)是一个技术文章的网站,时常会更新新的技术文章后然后推送给订阅者,我们来看看这个简单的需求如何通过观察者模式去实现.
public class Coder implements Observer{
public String name;
public Coder(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("Hi, "+name+" , DevTechFrontier 更新啦,内容 :"+arg);
}
@Override
public String toString() {
return "Coder{" +
"name='" + name + '\'' +
'}';
}
}
public class DevTechFrontier extends Observable {
public void postNewPublication(String content){
//标示状态和内容发生改变
setChanged();
//通知所有观察者
notifyObservers();
}
}
DevTechFrontier devTechFrontier = new DevTechFrontier();
Coder misimple = new Coder("mr.simple");
Coder coder1 = new Coder("coder-1");
Coder coder2 = new Coder("coder-2");
Coder coder3 = new Coder("coder-3");
devTechFrontier.addObserver(coder1);
devTechFrontier.addObserver(coder2);
devTechFrontier.addObserver(coder3);
devTechFrontier.postNewPublication("新的一起开发技术前线周报发布啦");
Observer和Observable是JDK中的内置类型,可见观察者模式是非常重要的,这里Observer是抽象的观察者角色,Coder扮演的是具体观察者角色;Observable是抽象主题角色,DevTechFrontier则是具体的主题角色。DevTechFrontier通过addObserver添加Coder到它的观察者列表中,一旦有文章更新便会通知所有列表中观察者对象.
Android中的观察者模式
ListView添加数据后,会调用Adapter的notifyDataSetChanged()方法,这是为什么呢?今天就来揭开它的神秘面纱。
第一步我们就跟进这个方法
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//数据集观察者
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
}
我们再跟进mDataSetObservable.notifyChanged()中去看看:
public class DataSetObservable extends Observable {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
调用notifyChanged后它会去通知mObservers这个观察者集合数据发生改变。这些观察者是ListView通过setAdapter方法设置Adapter产生的, 我们看看相关的代码:
@Override
public void setAdapter(ListAdapter adapter) {
//如果有了一个Adapter,那么先注销该Adapter对应的观察者
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
//注意这里,创建一个数据观察
mDataSetObserver = new AdapterDataSetObserver();
//将这个观察者注册到Adapter中,实际上是注册到mDataSetObservable中
mAdapter.registerDataSetObserver(mDataSetObserver);
//代码省略
} else {
//代码省略
}
}
这下我们观察者,被观察者都有了,它们怎么进行注册的也知道了,最后看看AdapterDataSetObserver到底是什么.它是AdapterView.java的一个内部类
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
}
当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者(AdapterDataSetObserver)的onChanged方法,在onChanged函数中又会调用ListView重新布局的函数使得Listview刷新界面,这就是一个观察模式.