设计模式之观察者模式

毫不夸张的说,观察者模式是最常用的设计模式之一了,通过使用观察者模式可以极大的解耦。 Android 四大组件之一的 BroadcastReceiver 就是一个耦合性非常低的观察者模式,最近大热的 RxJava 也是观察者模式的具体应用,所以我们学好观察者模式是非常有必要的,这篇博客将简要介绍观察者模式的概念、使用场景、优缺点,最后还会介绍 Android 源码中对于观察者模式的应用。
observer_uml.png

观察者模式的定义

观察者模式定义了一种一对多的依赖关系,让多个观察者(Observer)同时监听一个被观察者(Observable,有时候也叫做 Subject),这个被观察者在状态变化时会通知所有订阅的观察者,使得观察者能够更新自己。可以看到 Android 中的 BroadcastReceiver 就是一个观察者模式。

观察者模式的 UML

从上面的 UML 图可以看到,观察者模式涉及到了四个角色,分别是:
Subject 或 Observable :抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
ConcreteSubject 或 ConcreteObservable : 将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。 具体主题角色又叫做具体被观察者 ( Concrete Observable ) 角色。
Observer : 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
ConcreteObserver : 存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

下面是观察者模式的一个简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public interface Observer {
/**
* 更新接口
* @param state 更新的状态
*/

public void update(String state);
}

public abstract class Subject {
/**
* 用来保存注册的观察者对象
*/

private List<Observer> list = new ArrayList<Observer>();
/**
* 注册观察者对象
* @param observer 观察者对象
*/

public void attach(Observer observer){

list.add(observer);
System.out.println("Attached an observer");
}
/**
* 删除观察者对象
* @param observer 观察者对象
*/

public void detach(Observer observer){

list.remove(observer);
}
/**
* 通知所有注册的观察者对象
*/

public void nodifyObservers(String newState){

for(Observer observer : list){
observer.update(newState);
}
}
}

public class ConcreteObserver implements Observer {
//观察者的状态
private String observerState;

@Override
public void update(String state) {
/**
* 更新观察者的状态,使其与目标的状态保持一致
*/

observerState = state;
System.out.println("状态为:"+observerState);
}

}

public class ConcreteSubject extends Subject{

private String state;

public String getState() {
return state;
}

public void change(String newState){
state = newState;
System.out.println("主题状态为:" + state);
//状态发生改变,通知各个观察者
this.nodifyObservers(state);
}
}

public class Client {

public static void main(String[] args) {
//创建主题对象
ConcreteSubject subject = new ConcreteSubject();
//创建观察者对象
Observer observer = new ConcreteObserver();
//将观察者对象登记到主题对象上
subject.attach(observer);
//改变主题对象的状态
subject.change("new state");
}
}

观察者模式如此重要以至于 JDK 已经为我们内置了 Observable 和 Observer,下面是 Observable 和 Observer 的源码也很简单,基本上和我们自己实现的没有什么不同。 以后我们如果需要使用观察者模式就可以相应的实现 Observer 继承 Observable 就可以了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public interface Observer {
void update(Observable observable, Object data);
}

public class Observable {

List<Observer> observers = new ArrayList<Observer>();

boolean changed = false;

public Observable() {
}

public void addObserver(Observer observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
observers.add(observer);
}
}

protected void clearChanged() {
changed = false;
}

public int countObservers() {
return observers.size();
}

public synchronized void deleteObserver(Observer observer) {
observers.remove(observer);
}

public synchronized void deleteObservers() {
observers.clear();
}

public boolean hasChanged() {
return changed;
}

public void notifyObservers() {
notifyObservers(null);
}

public void notifyObservers(Object data) {
int size = 0;
Observer[] arrays = null;
synchronized (this) {
if (hasChanged()) {
clearChanged();
size = observers.size();
arrays = new Observer[size];
observers.toArray(arrays);
}
}
if (arrays != null) {
for (Observer observer : arrays) {
observer.update(this, data);
}
}
}

protected void setChanged() {
changed = true;
}
}

Android 源码中的观察者模式

Android 源码中对于观察者模式的典型应用必属 ListView 了。我们在使用 ListView 时一般都会继承 BaseAdapter 实现自己的 Adapter ,然后将 Adapter 设置给 ListView ,在 数据更新时会调用 Adapter 的 notifyDataSetChanged 方法进行界面更新,首先我们看一下 BaseAdapter 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();

public boolean hasStableIds() {
return false;
}

public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}

public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}

public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}

public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}

public boolean areAllItemsEnabled() {
return true;
}

public boolean isEnabled(int position) {
return true;
}

public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getView(position, convertView, parent);
}

public int getItemViewType(int position) {
return 0;
}

public int getViewTypeCount() {
return 1;
}

public boolean isEmpty() {
return getCount() == 0;
}
}

我们继承 BaseAdapter 时覆写的一些方法都可以看到,但是这次我们的重点是 notifyDataSetChanged 方法, notifyDataSetChanged 方法又调用了 DataSetObservable 的 notifyChanged 方法。从这些类的命名上我们能够明显看出观察者模式的痕迹。 很明显 DataSetObservable 就是一个具体的 Observable 实现。下面我们再看看 DataSetObservable 的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class DataSetObservable extends Observable<DataSetObserver> {
public void notifyChanged() {
synchronized(mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}

public void notifyInvalidated() {
synchronized (mObservers) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onInvalidated();
}
}
}
}

public abstract class Observable<T> {
protected final ArrayList<T> mObservers = new ArrayList<T>();

public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}

public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}

public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}

从以上代码我们应该能明显的看出这是一个典型的 Observable 实现。 DataSetObservable 的 notifyChanged 进一步调用了 DataSetObserver 的 onChanged 方法。看到这里我们应该明白了 DataSetObserver 便是 Observer 的具体实现。 DataSetObserver 订阅 DataSetObservable ,在 DataSetObservable 的状态改变时会去通知 DataSetObserver 。 可以看到这确实是一个典型的 观察者模式。现在我们要问的是 DataSetObserver 是什么时候订阅 DataSetObservable的,下面我们看一下 ListView 的 setAdapter 方法源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}

resetList();
mRecycler.clear();

if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}

mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;

// 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);

mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

int position;
if (mStackFromBottom) {
position = lookForSelectablePosition(mItemCount - 1, false);
} else {
position = lookForSelectablePosition(0, true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);

if (mItemCount == 0) {
// Nothing selected
checkSelectionChanged();
}
} else {
mAreAllItemsSelectable = true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}

requestLayout();
}

从上面的代码我们可以看到,在 setAdapter 时会 new AdapterDataSetObserver ,然后会调用 Adapter 的 registerDataSetObserver 方法,我们要注意的是 setAdapter 方法的参数是 ListAdapter ,而 BaseAdapter 实现了 ListAdapter 接口。又其中 AdapterDataSetObserver 最终继承了 DataSetObserver 。因此 registerDataSetObserver 的最终实现是 BaseAdapter ,在上面我们已经看到在 BaseAdapter 的 registerDataSetObserver 方法中又调用了 DataSetObservable 的 registerObserver 方法,也就是说 DataSetObserver 是在 setAdapter 时订阅了 DataSetObservable 的。
下面我们再看看 AdapterDataSetObserver 的源码, AdapterDataSetObserver 是 ListView 的父类 AbsListView 的内部类, AbsListView.AdapterDataSetObserver 又继承了 AdapterView.AdapterDataSetObserver ,其中 AdapterView 是 AbsListView 的父类,因此我们直接看 AdapterView.AdapterDataSetObserver 的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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();
}

@Override
public void onInvalidated() {
mDataChanged = true;

if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}

// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;

checkFocus();
requestLayout();
}

public void clearSavedState() {
mInstanceState = null;
}
}

我们看到在 AdapterView.AdapterDataSetObserver 的 onChanged 方法中调用了 requestLayout() 方法进行重新布局,到这里我们就明白了 ListView 更新的整个过程。 在 ListView 调用 setAdapter 时,会调用 BaseAdapter 的 registerDataSetObserver 方法,BaseAdapter 进一步调用了 DataSetObservable 的 registerObserver 方法,被注册的就是 AdapterView.AdapterDataSetObserver 的实例,这便是 Observer 订阅 Observable 的过程。在数据集发生变化时我们调用 Adapter 的 notifyDataSetChanged 方法, notifyDataSetChanged 会进一步调用 DataSetObservable 的 notifyChanged 方法通知所有的 Observer ,而 Observer 的最终实现者是 AdapterView.AdapterDataSetObserver ,在 AdapterDataSetObserver 调用了 requestLayout 方法进行重新布局,更新界面。

总结

观察者模式的主要作用就是对象的解耦,将具体的观察者和被观察者隔离,它们只依赖 Observable 和 Observer 抽象。 Android 源码中的 ListView 就是应用了 观察者模式。