定义
定义对象之间一对多的依赖关系,使得每当一个对象发生变化时,则所有依赖于它的对象都会得到通知并自动更新。这个模式最重要的是可以做到被观察者和观察者的解耦,使他们的依赖性变小。而这个模式最常用于GUI系统以及订阅-发布系统,如期刊或者邮件的订阅等。
UML类图
观察者设计模式观察者设计模式,主要有被观察者和观察者角色
-
Subject:
抽象主题,是被观察者的角色。其有添加和删除观察者的方法,并且把观察者保存在一个集合中。
-
ConcreteSubject
具体主题,抽象主题的实现类。定义通知观察者的方法,当其状态发生变化时,循环遍历观察者调用其update方法通知观察者更新。
-
Observer
抽象观察者,定义了更新方法,得到被观察者变化的通知时更新自己。
-
ConcreteObserver
具体的观察者,实现了具体的更新方法逻辑。
示例
当我们订阅了一种期刊,每当期刊更新时,发版商都会将最新的期刊邮寄给我们。这就是经典的发布订阅关系,即观察者设计模式。
/**
* 期刊发布商角色
* 即被观察者,当发布了新的期刊时通知订阅者
*/
public class Publisher extends Observable {
//发布新期刊
public void publishNew(String name){
setChanged();
notifyObservers(name);
}
}
/**
* 订阅者角色
* 即观察者,收到更新
*/
public class Person implements Observer {
private String name;
public Person(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + "收到发布商的更新,内容为" + arg);
}
}
public static void main(String []args){
Publisher publisher = new Publisher();
publisher.addObserver(new Person("小明"));
publisher.addObserver(new Person("小李"));
publisher.addObserver(new Person("小王"));
//发布更新
publisher.publishNew("航空知识第12期发布");
}
小王收到发布商的更新,内容为航空知识第12期发布
小李收到发布商的更新,内容为航空知识第12期发布
小明收到发布商的更新,内容为航空知识第12期发布
Android源码中的观察者模式
在使用ListView时,当数据源发生变化时,我们会调用adapter.notifyDataSetChanged()刷新列表,ListView就是使用了观察者设计模式。
首先看一下notifyDataSetChanged()的实现
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
这里调用了mDataSetObservable的notifyChanged方法,而这个方法遍历了观察者对象,并调用了观察者的onChanged()方法,通知其发生了变化
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
* Called when the contents of the data set have changed. The recipient
* will obtain the new contents the next time it queries the data set.
*/
public void notifyChanged() {
synchronized(mObservers) {
// since onChanged() is implemented by the app, it could do anything, including
// removing itself from {@link mObservers} - and that could cause problems if
// an iterator is used on the ArrayList {@link mObservers}.
// to avoid such problems, just march thru the list in the reverse order.
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
//...
}
DataSetObservable是一个数据集的被观察者,定义在BaseAdapter中
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);
}
/**
* Notifies the attached observers that the underlying data has been changed
* and any View reflecting the data set should refresh itself.
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
/**
* Notifies the attached observers that the underlying data is no longer valid
* or available. Once invoked this adapter is no longer valid and should
* not report further data set changes.
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
...
}
BaseAdapter中有registerDataSetObserver方法接受一个DataSetObserver数据集观察者,并将其注册到DataSetObservable中,这样就形成了观察者和被观察者订阅关系。而这个方法在ListView的setAdapter方法中被调用并传入了AdapterDataSetObserver类型的观察者,如下
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
//...
} else {
//...
}
requestLayout();
}
这里实例化了一个AdapterDataSetObserver类型的观察者,其实现如下
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();
}
//...
}
在它的onChange方法中,最终调用了requestLayout()方法使ListView进行了重新布局。
网友评论