设计模式--观察者模式

一、问题的提出
举例:
WeatherData是气象站获取数据的对象,通过他获取到数据后更新到不同的布告板上。
WeatherData有三个方法:getTemperature(), getHumidity(), getPressure()用于获取温度,湿度,气压值
当新数据获得后,可以调用WeatherData中的measurementsChanged()方法,更新到不同的布告板上。
错误的设计:


image.png

二、初步认识
首先看看报纸的订阅是如何进行的:
1、报社的业务是出版报纸
2、向某家报社订阅报纸,只要他们有新报纸出版,就给你送来,只要你是他们的订户,就会一直收到新报纸
3、当你不想再看报纸时,取消订阅,他们就不会再送新报纸了
4、只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅
出版者(被观察者Subject) + 订阅者(观察者Observe) = 观察者模式


image.png

观察者模式--在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
image.png

三、模式类图
image.png

四、优点
观察者模式提供了一种对象设计,让被观察者和观察者之间松耦合,他们可以交互,但是不需要知道彼此的细节,被观察者只需要知道观察者实现了Oberver接口,并不需要知道观察者的具体类是谁,做了什么细节操作。因为被观察者唯一依赖的是一个实现Observer接口的对象列表,所以可以随时增加观察者,也可以用新的观察者取代现有的观察者,删除观察者。当有新的观察者出现时,无需修改被观察者代码,被观察者只把消息发送给观察者,观察者如何处理,被观察者并不关心。
Tips:

松耦合的设计能让我们建立有弹性的OO系统,能应对变化,对象之间的相互依赖降到了最低。

五、解决最初气象站的问题
1、首先建立被观察者Subject接口,用于注册、删除、通知观察者


image.png

2、在WeatherData中实现被观察者Subject接口


image.png

3、建立布告板实现观察者Observer接口(有多个布告板(观察者),实现的方法类似)
image.png

六、观察者模式在android中的应用
1、通常在ListView的内容变化时,会调用notifyDataSetChanged()这个方法,然后ListView里面的数据就会进行更新。感觉像是观察者模式,具体是不是再看看源码。

package android.widget; 
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); 
  } 
  ...... 
  public void notifyDataSetChanged() { 
    mDataSetObservable.notifyChanged(); 
  } 
  ......
}

这是一个观察者模式,里面提供了注册和注销观察者以及通知观察者的方法。
这些方法是通过DataSetObservable这个类调用的:

package android.database;public class DataSetObservable extends Observable {   
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    ......
}

这个类继承自Observable,Observable中有一个protected final ArrayList mObservers = new ArrayList();,用来保存注册的观察者。mDataSetObservable.registerObserver(observer)和mDataSetObservable.unregisterObserver(observer)分别就是增加和删除。
在notifyChanged方法中,循环这个集合,调用每一个观察者的onChanged()方法。
那么ListView和Adapter什么时候成了订阅关系。
在ListView的setAdapter()中

public class ListView extends AbsListView {
    public void setAdapter(ListAdapter adapter) {
        //如果已经有了一个adapter,注销这个adapter之前的观察者,
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
       ......
       if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            //将新的adapter赋给mAdapter
            mAdapter = adapter;
        }
        ......
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            //保存之前的数据个数
            mOldItemCount = mItemCount;
            //获取新的个数
            mItemCount = mAdapter.getCount();
            checkFocus();
            //创建数据集观察者
            mDataSetObserver = new AdapterDataSetObserver();
            //注册观察者
            mAdapter.registerDataSetObserver(mDataSetObserver);
            ...
            }
        } else {
            ...
        }

        requestLayout();
    }
}

AdapterDataSetObserver是ListView的父类AbsListView的内部类

package android.widget;
public abstract class AbsListView extends AdapterView implements TextWatcher,
        ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
        ViewTreeObserver.OnTouchModeChangeListener,
        RemoteViewsAdapter.RemoteAdapterConnectionCallback {
       class AdapterDataSetObserver extends AdapterView.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
        ······
    }
}

AdapterDataSetObserver是AdapterView.AdapterDataSetObserver的子类,所以要看super.onChanged()

package android.widget;
public abstract class AdapterView extends ViewGroup {
    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();
        }
        ......
    }
}

整理一下:当ListView数据变化时,调用Adapter的notifyDataSetChange方法,这个方法调用DataSetObservable的notifyChanged方法,这个方法又会调用所有观察者的onChanged方法,onChanged再调用重新布局View的方法,完成刷新数据的功能。

你可能感兴趣的:(设计模式--观察者模式)