观察者
意图
观察者模式中有两种类型的对象存在:被观察者(发布-Publish),观察者(订阅者-Subscribe)。它们之间存在一(Publish)对一(Subscribe)或者一(Publish)对多(Subscribe)的关系。当Publish的状态发生变化的时候,所有依赖Publish的Subscribe都会接收到通知,并发生自动更新事件(Event)。
别名
依赖(Dependents),发布-订阅(Publish-Subscribe)。
动机
将一个系统分割成一系列相互协作(工作)的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合(增大了类间的耦合性),因为这会导致类的可重用性降低。
适用性
在以下情景可以使用观察者模式
当一个抽象模型有两个方面,其中一个方面依赖另一个方面。将这二者封装在独立的对象中以使它们各自独立地改变和复用。
当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变的时候。
当一个对象必须通知其他对象,而它又不能假定其他对象是谁。换而言之,你不希望这些对象紧密联系 。
参与者
Subject(目标,即被观察者)
- 目标知道它观察者,可有任意多个观察者观察同一个目标。
- 提供注册和删除观察者对象的接口
Observer(观察者)
- 为那些在被观察者发生改变时需获得通知的对象定义一个更新接口。
ConcreteSubject(具体目标)
- 将有关状态存入ConcreteObserver对象
- 当它的状态发生改变时,向它的观察者发出通知
ConcreteObserver(具体观察者)
- 维护一个指向ConcreteSubject对象的引用
- 存储目标的状态,存储的状态必须与目标的状态具有一致性
- 实现Observer更新接口以使自身状态与目标状态保持一致
实现
我们可以拿手机关机(publish)-当前程序保存数据(Subcribe)的事件来说明,手机状态是被观察者,当前开启的程序是观察者,当手机发出关机信号时,程序接收到信号广播开始在关机前保存现有数据。
Subject
/**
* @author Created by MadJieJie on 2017/4/20-16:00.
* @brief
* @attention
*/
public interface Subject
{
/**
* Add Observer element.
* @param observer register element
*/
void attach( Observer observer );
/**
* Remove Observer element.
* @param observer unregister element.
*/
void detach( Observer observer );
/**
* Notify Observer's elements that subject's state has changed.
* @param message send message
*/
void notify(String message);
}
ConcreteSubject
import java.util.ArrayList;
import java.util.List;
/**
* @author Created by MadJieJie on 2017/4/20-16:00.
* @brief
* @attention
*/
public class ConcreteSubject implements Subject
{
private List<Observer> mProgramList = new ArrayList<>();
@Override
public void attach ( Observer observer )
{
mProgramList.add(observer);
}
@Override
public void detach ( Observer observer )
{
mProgramList.remove(observer);
}
@Override
public void notify ( String message )
{
for(Observer observer: mProgramList )
{
observer.saveData(message);
}
}
}
Observer
/**
* @author Created by MadJieJie on 2017/4/20-16:00.
* @brief
* @attention
*/
public interface Observer
{
/**
* Save data before cellphone close.
*/
void saveData(String message);
}
ConcreteObserver
import java.util.ArrayList;
import java.util.List;
/**
* @author Created by MadJieJie on 2017/4/20-16:00.
* @brief
* @attention
*/
public class ConcreteSubject implements Subject
{
private List<Observer> mProgramList = new ArrayList<>();
@Override
public void attach ( Observer observer )
{
mProgramList.add(observer);
}
@Override
public void detach ( Observer observer )
{
mProgramList.remove(observer);
}
@Override
public void notify ( String message )
{
for(Observer observer: mProgramList )
{
observer.saveData(message);
}
}
}
协作
1.得到Subject的对象,然后Subject对象调用attach方法分别赋入Observer对象qqObserver、weiXinObserver使它们订阅到被观察者。
2.被观察者Subject对象发出Notify(“close”)事件,分别发送给它们的订阅者们,订阅者都接受到事件,进行更新方法-saveData().
Android中的Observer
Android中大量使用了观察者模式,像许多点击事件OnClickListener、OnItemClickListener、android.database.Observable等;还有第三方开源库EventBus、RxJava、RxAndroid都是基于观察者模式设计的。下面我将拿BaseAdapter的中的notifyDataSetChanged()方法来做例子
BaseAdapter明显是被观察者(Observable),实现了被观察者的方法-注册、注销和通知。
BaseAdapter
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
private final DataSetObservable mDataSetObservable = new DataSetObservable();
//register
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
//unregister
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();
}
......
}
进入查看mDataSetObservable.notifyChanged()的实现,它是使用同步锁锁住一个ArrayList对象,并对它的元素遍历实现onChanged()方法。
mDataSetObservable.notifyChanged()
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();
}
}
}
}
再进入它的该类的父类看到了具体被观察者(ConcreteObservable)
Observable
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
* once and will never be null.
*/
protected final ArrayList<T> mObservers = new ArrayList<T>();
/**
* Adds an observer to the list. The observer cannot be null and it must not already
* be registered.
* @param observer the observer to register
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is already registered
*/
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);
}
}
/**
* Removes a previously registered observer. The observer must not be null and it
* must already have been registered.
* @param observer the observer to unregister
* @throws IllegalArgumentException the observer is null
* @throws IllegalStateException the observer is not yet registered
*/
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);
}
}
/**
* Remove all registered observers.
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
ListView中setAdapter方法中诠释了注册的流程-》
1.先判断mAdapter&mDataSetObserver 不为空后,注销之前的观察者mAdapter.unregisterDataSetObserver(mDataSetObserver);
2.然后判断mAdapter不为NULL后创建新的观察者mDataSetObserver = new AdapterDataSetObserver();
3.注册新的观察者mAdapter.registerDataSetObserver(mDataSetObserver)。
setAdapter
/**
* Sets the data behind this ListView.
*
* The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
* depending on the ListView features currently in use. For instance, adding
* headers and/or footers will cause the adapter to be wrapped.
*
* @param adapter The ListAdapter which is responsible for maintaining the
* data backing this list and for producing a view to represent an
* item in that data set.
*
* @see #getAdapter()
*/
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);//Unregister Observer
}
.....
// 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();//Create Observer object.
mAdapter.registerDataSetObserver(mDataSetObserver);//Register Observer.
.....
}
requestLayout(); //Request layout again.
}
然后我们进入观察者(Observer)类AdapterDataSetObserver中看具体实现的更新方法onChanged();暂时看不到什么,但它调用了父类的super.onChanged(),进父类看
onChanged()
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
@Override
public void onChanged() {
super.onChanged();
if (mFastScroller != null) {
mFastScroller.onSectionsChanged();
}
}
......
}
进入父类我们看到了具体的观察者(ConcreteObserver),也看到了更新方法更为具体的实现,当数据源发生变化的时候,我们调用Adapter的notifyDataSetChanged()会导致数据重新填充入View&调用requestLayout()方法布局重构。
super.onChanged()
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();//Request layout again.
}
...
public void clearSavedState() {
mInstanceState = null;
}
}
效果
1.被观察者和观察者之间的抽象耦合
一个被观察者拥有一系列的观察者,每个观察者都符合Observer类的简单接口,被观察者自身高内聚,与其他观察者无过多的耦合,这样它们在系统中可以属于不同的层次,一个较低层次的被观察者可以与处于较高层次的观察者通信并通知它,这样保持了系统层次的完整。
2.支持广播通信
被观察者对象不需要关心有多少观察者,它只需要向登记的观察者发送通知。
3.过度耦合
由于被观察者和观察者的高度耦合,一个观察者并不知道其他观察者存在,若一个观察者对被观察者进行改变,则会影响所有依赖它的观察者,并且这种错误难以追踪,故观察者最好不要改变被观察者。由于程序我们习以为常顺序执行,多个观察者会导致我们开发和调试难度增大,而一个被观察者的卡顿会影响整体的效率,我们一般采用异步解决。