一、 定义
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
二、角色
抽象主题角色(Subject)
抽象主题角色把所有观察者对象的引用保存到一个聚集里(例如ArrayList对象),每个主题都可以有任何数量的观察者。抽象主题提供增加、移除观察者对象的方法。也叫抽象被观察者角色。
具体主题角色(ConcreteSubject)
在具体主题内部状态改变时,给所有登记过的观察者发出通知。也叫具体被观察者角色。
抽象观察者角色(Observer)
为所有的具体观察者定义一个更新接口,在得到主题的通知时更新自己。
具体观察者角色(ConcreteObserver)
实现抽象观察者角色的更新接口,以便在得到主题更改通知时及时更新自身状态。
相关的类图:
三、简单代码实现
3.1 创建抽象观察者
/**
* 抽象观察者角色
*
* Created by Administrator on 2017/10/26.
*/
public abstract class Observer {
public abstract void update(String result);
protected String name;
public Observer(String name) {
this.name = name;
}
}
3.2 创建具体观察者
/**
* 具体的观察者
*
* Created by Administrator on 2017/10/26.
*/
public class ConcreteObserver extends Observer {
private static final String TAG = ConcreteObserver.class.getSimpleName();
public ConcreteObserver(String name) {
super(name);
}
@Override
public void update(String result) {
Log.e(TAG,"name:"+name+";"+result);
}
}
3.3 创建抽象主题(抽象被观察者)
/**
* 抽象主题角色(抽象被观察者)
*
* Created by Administrator on 2017/10/26.
*/
public abstract class Subject {
private Vector<Observer> vectors = new Vector<Observer>();
// 注册观察者
public void attach(Observer observer){
vectors.add(observer);
}
// 反注册观察者
public void detach(Observer observer){
if (vectors.contains(observer)){
vectors.remove(observer);
}
}
//通知所有注册的观察者,状态改变
public void notifyObserver(String message){
for(Observer o:vectors){
o.update(message);
}
}
public abstract void doSomething();
}
3.4 创建具体主题(具体的观察者)
/**
* Created by Administrator on 2017/10/26.
*/
public class ConcreteSubject extends Subject {
@Override
public void doSomething() {
}
}
3.5 具体调用
ConcreteObserver observer1 = new ConcreteObserver("观察者1");
ConcreteObserver observer2 = new ConcreteObserver("观察者2");
// 创建具体主题(被观察者)
ConcreteSubject concreteSubject = new ConcreteSubject();
// 注册观察者
concreteSubject.attach(observer1);
concreteSubject.attach(observer2);
concreteSubject.notifyObserver("来学习设计模式了");
// 反注册观察者
// concreteSubject.detach(observer1);
// concreteSubject.detach(observer2);
执行结果:
10-26 10:34:09.771 1904-1904/com.observerpattern E/ConcreteObserver: name:观察者1;来学习设计模式了
10-26 10:34:09.771 1904-1904/com.observerpattern E/ConcreteObserver: name:观察者2;来学习设计模式了
四、Android中的观察者模式
典型的安卓中使用观察者模式的有:ListView的刷新,BroadcastReceiver。下面以ListView的刷新来分析。
4.1 ListView的刷新都是调用notifyDataSetChanged,具体分析下
Adapter关联了一个被观察者,这里就是被观察者通知观察者,数据更新。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
// 其他逻辑...
/**
* 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();
}
// 其他逻辑...
}
4.2 notifyChanged内部实现
被观察者内部通知:遍历所有观察者,通知数据刷新
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();
}
}
}
4.3 Adapter关联被观察者与观察者与被观察者的注册
setAdapter的代码就不贴了,只贴关键部分
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
实际上是用Adapter做了一个传递。
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
4.4 具体的刷新操作
这里就要分析AdapterDataSetObserver了,因为这是观察者,观察者接收到通知后会进行数据刷新。
先在onChange方法中获取数据个数,然后再去requestLayout,重新布局AdapterViewCompat。
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 (AdapterViewCompat.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterViewCompat.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterViewCompat.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterViewCompat.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;
}
}
五、总结
5.1 优点
观察者和被观察者是抽象耦合,容易扩展,修改其中一方可以不影响另一方的改变。
5.2 缺点
- 由于通知是使用的简单循环,有可能某个方法内部出现问题导致阻塞或者异常(改为异步),观察者较多时,效率问题也有待提升。
5.3 使用场景
- 一个对象状态的更新,需要同步给其他对象时(比如推送的消息列表实时更新、消息数同步增加)
- 对象仅需要将自己的更新通知给其他对象,而不需要知道其实现细节。