观察者模式的介绍
观察者模式是一个使用频率非常高的模式,它最常用的地方是GUI
系统、订阅-发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得他们之间的依赖性更小,甚至做到毫无依赖。以GUI
系统来说,应用的UI
具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI
系统需要一套机制来应对这种情况,使得UI
层与业务层 逻辑解耦,观察者 模式此时就派上用场了。
观察者模式的定义
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
观察者模式的使用场景
- 关联行为场景,需要注意的是,关联 行为是可拆分的,而不是“组合”关系
- 事件多级触发场景
- 跨系统的消息交换场景,如消息队列、事件总线的处理机制
观察者模式的UML类图
观察者模式.png角色介绍:
- Subject: 抽象主题,也就是被观察者(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject: 具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,会给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(ConcreteObservable)角色。
- Observer: 抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更改自己。
- ConcreteObserver: 具体的观察者,该角色实现观察者角色所定义的更新 接口,以便在主题的状态发生变化时更新自身的状态。
观察者模式的简单实现
举个例子:程序员会关注技术公众号,当公众号发文时,每个关注的程序员就会收到推送的文章,这个模式叫做发布-订阅模式,它的另一个名称是观察者模式,下面我们简单模拟一下公众号的发布-订阅过程。
/**
* 程序员是观察者
*/
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("hello "+name+" 内容:"+arg);
}
@Override
public String toString() {
return "程序员:"+name;
}
}
public class PublicDev extends Observable{
public void postNewPublication(String content){
//标识状态或者内容发生改变
setChanged();
//通知所有观察者
notifyObservers(content);
}
}
@org.junit.Test
public void test() {
//被观察者角色
PublicDev dev = new PublicDev();
//创建观察者
Coder coder1 = new Coder("coder1");
Coder coder2 = new Coder("coder2");
Coder coder3 = new Coder("coder3");
//将观察者注册到可观察对象的观察者列表中
dev.addObserver(coder1);
dev.addObserver(coder2);
dev.addObserver(coder3);
dev.postNewPublication("公众号更新文章啦");
}
结果:
hello coder3 内容:公众号更新文章啦
hello coder2 内容:公众号更新文章啦
hello coder1 内容:公众号更新文章啦
可以看到所有订阅公众号的程序员都收到了更新消息,一对多的订阅-发布系统就完成了。
Observer
和Obserable
是JDK
中的内置类型,可见观察者模式是非常重要的,这里Observer
是抽象的观察者角色,Coder
扮演的是具体的观察者角色,Obserable
对应的是抽象的主题角色,PublicDev
则是具体的主题角色,Coder
是具体观察者,他们订阅了PublicDev
这个具体的可观察对象,当PublicDev
有更新时,会遍历所有观察者(这里是Coder
) 然后给这些观察者发布一个更新的消息,即调用Coder
的update
方法,这样就达到了一对多的通知功能。在这个过程中,通知系统都是依赖Observer
和Obserable
这些抽象类,因此,对于Coder
和PublicDev
完全没有耦合,保证了订阅系统的灵活性、可扩展性。
Android源码中的观察者模式
ListView是Android中的重要的控件之一,而ListView最重要的一个功能就是Adapter。通常,在我们往ListView添加数据后,都会调用Adapter的notifyDataSetChange方法,这是为什么呢?我们看 下面的分析。
首先我们跟进这个方法notifyDataSetChange
,这个方法定义在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();
}
}
一看BaseAdapter
代码就大体有了这么一个认识:BaseAdapter
是一个观察者模式!那么BaseAdapter
是如何运作的?这些观察者又是什么呢?我们先到mDataSetObservable.notifyChanged();
方法看看:
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
//调用每个观察者 对象的onChanged方法来通知他们被观察者发生了变化
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
}
这个代码很简单,就是在mDataSetObservable.notifyChanged()
中遍历所有观察者,并且调用它们的onChange
方法。从而告知观察者发生了变化。那么这些观察者是从哪里来的呢?其实这些观察者就是ListView
通过setAdater
方法设置Adapter
产生的,我们看看相关代码:
@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();
//将这个观察者注册到Adapter中,实际上是添加到DataSetObserable中
mAdapter.registerDataSetObserver(mDataSetObserver);
}
requestLayout();
}
从程序中可以看到,在设置Adapter
时构建一个AdapterDataSetObserver
,这就是上面所说的观察者,最后,将这个观察者注册到Adapter
中,这样我们的被观察者、观察者都有了。这个时候有个疑问,AdapterDataSetObserver
是什么?它是如何运作的?那么就先来看看AdapterDataSetObserver
,AdapterDataSetObserver
是定义在ListView
的父类AbsListView
中,具体代码如下:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
@Override
public void onInvalidated() {
super.onInvalidated();
if (mFastScroll != null) {
mFastScroll.onSectionsChanged();
}
}
}
他又继承自AbsListView的父类AdapterView的AdapterDataSetObserver,具体代码如下;
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
//获取Adapter中的数量
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();
}
...
public void clearSavedState() {
mInstanceState = null;
}
}
到这里我们知道了,当ListView
的数据发生变化时,调用Adapter
的notifyDataSetChange
方法,这个方法又会调用DataSetChange
的notifyChange
方法,这个方法会调用所有观察者(AdapterDataSetObserver
)的onChange
方法,在onChange
方法中又会调用ListView
重新布局的方法使得ListView
刷新界面,这就是一个观察者模式!
最后,我们在整理一下这个过程,AdapterView
中有一个内部类AdapterDataSetObserver
,在ListView
中设置Adapter
时会构建一个AdapterDataSetObserver
,并且注册到Adapter
中,这就是一个观察者,而Adapter
中包含一个数据集可观察者DataSetObservable
,在数量发生变化时,开发者手动调用Adapter
的 notifyDataSetChange
,而notifyDataSetChange
实际上会调用DataSetObservable
的notifyChanged
方法,该方法会遍历所有观察者的onChanged
函数。在AdapterDataSetObserver
的onChanged
方法中会获取Adapter
中数据集的新数量,然后调用ListView
的requestLayout
方法重新进行布局,更新用户界面。
总结
观察者模式的主要作用就是对象解耦,将观察者与被观察者完全隔离,只依赖于Observer
和Observable
抽象,例如,ListView
就是运用Adapter
和观察者模式使得它的可扩展性、灵活性非常强,而 耦合度去很低,这是设计模式在Android
源码中优秀运用的典范。
优点
- 观察者和被观察者之间是抽象耦合,应对业务变化;
- 增强系统的灵活性、可扩展性。
缺点
在应用观察者模式是需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者、开发和调试等内容会比较复杂,而且在Java
中消息的通知默认是顺序执行,一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般考虑采用异步的方式。
Demo
参考
《Android源码设计模式》
网友评论