設計模式觀察者模式

1.觀察者模式的定義及使用場景
觀察者模式是一個使用率非常高的模式,它最常用的地方是GUI系統,訂閱-發布系統。因為這個模式的一個重要作用就是解耦,將被觀察者和觀察者解耦,使得它們之間的依賴性更小。
定義:
定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所以依賴于它的對象都會得到通知并被自動更新
使用場景:
關聯行為場景,需要注意的是,關聯行為時可拆分的,而不是“組合”關系;
事件多級觸發場景
跨系統的消息交換場景,如消息隊列、事件總線的處理機制

Paste_Image.png

2. 觀察者模式的優缺點
2.1優點
觀察者和被觀察者之間是抽象解耦,應對業務變化
增強系統靈活性、可擴展性
2.2缺點
在應用觀察者模式時需要考慮一下開發效率和運行效率問題,程序中包括一個被觀察者、多個觀察者、開發和調試等內容會比較復雜,而且在Java中消息的通知默認是順序執行,一個觀察者卡頓,會影響整體的執行效率,在這種情況下,一般考慮采用異步的方式
3. 觀察者模式的實現方式

public interface Observer {
    //更新方法
    public void  update();
}```

public class ConcreteObsever implements Observer {
@Override
public void update() {
System.out.println("接收到信息,并進行處理!");
}
}```

public abstract class Subject {
    //定義一個觀察者數組
    private List<Observer> observers = new ArrayList<>();
    //增加一個觀察者
    public void addObserver(Observer o) {
        this.observers.add(o);
    }
    //刪除一個觀察者
    public void removeObserver(Observer o) {
        this.observers.remove(o);
    }
    //通知所有觀察者
    public void notifyObservers() {
        for (Observer o : this.observers) {
            o.update();
        }
    }
}```

public class ConcreteSubject extends Subject {
//具體的業務
public void doSomeThing() {
super.notifyObservers();
}
}```

public class Test {
    public static void main(String args[]) {
      //創建一個被觀察者
        ConcreteSubject subject=new ConcreteSubject();
        Observer obs=new ConcreteObsever();
        subject.addObserver(obs);
        subject.doSomeThing();
    }
}```
**4. 觀察者模式在Android中的實際應用**
RecycleView是Android中最重要的控件之一,而RecycleView最重要的一個功能就是Adapter。通過我們往RecycleView添加數據后,都會調用Adapter的notifyDataSetChanged()方法,這是為什么?
首先我們看下Adapter的實現,他是RecycleView的一個內部類。

public static abstract class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
private boolean mHasStableIds = false;
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(VH holder, int position);
public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
onBindViewHolder(holder, position);
}
public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
}
public final void bindViewHolder(VH holder, int position) {
holder.mPosition = position;
if (hasStableIds()) {
holder.mItemId = getItemId(position);
}
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
holder.clearPayload();
TraceCompat.endSection();
}
public int getItemViewType(int position) {
return 0;
}
public void setHasStableIds(boolean hasStableIds) {
if (hasObservers()) {
throw new IllegalStateException("Cannot change whether this adapter has " +
"stable IDs while the adapter has registered observers.");
}
mHasStableIds = hasStableIds;
}
public long getItemId(int position) {
return NO_ID;
}
public abstract int getItemCount();
public final boolean hasStableIds() {
return mHasStableIds;
}
public void onViewRecycled(VH holder) {
}
public boolean onFailedToRecycleView(VH holder) {
return false;
}
public void onViewAttachedToWindow(VH holder) {
}
public void onViewDetachedFromWindow(VH holder) {
}
public final boolean hasObservers() {
return mObservable.hasObservers();
}
public void registerAdapterDataObserver(AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
mObservable.unregisterObserver(observer);
}
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
}
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
}
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position, 1);
}
public final void notifyItemChanged(int position, Object payload) {
mObservable.notifyItemRangeChanged(position, 1, payload);
}
public final void notifyItemRangeChanged(int positionStart, int itemCount) {
mObservable.notifyItemRangeChanged(positionStart, itemCount);
}
public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
}
public final void notifyItemInserted(int position) {
mObservable.notifyItemRangeInserted(position, 1);
}
public final void notifyItemMoved(int fromPosition, int toPosition) {
mObservable.notifyItemMoved(fromPosition, toPosition);
}
public final void notifyItemRangeInserted(int positionStart, int itemCount) {
mObservable.notifyItemRangeInserted(positionStart, itemCount);
}
public final void notifyItemRemoved(int position) {
mObservable.notifyItemRangeRemoved(position, 1);
}
public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
mObservable.notifyItemRangeRemoved(positionStart, itemCount);
}
}```
我們可以看到notifyDataSetChange()中調用了mObservable.notifyChange()。繼續看AdapterDataObservable類

static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
}
}
public void notifyItemRangeInserted(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
}
}
public void notifyItemRangeRemoved(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
}
}
public void notifyItemMoved(int fromPosition, int toPosition) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
}
}
}```
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();
    }
    }
    }```
    AdapterDataObserver 的派生類RecyclerViewDataObserver: mObserver是RecycleView的一個變量
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();```

private class RecyclerViewDataObserver extends AdapterDataObserver {
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
if (mAdapter.hasStableIds()) {
// TODO Determine what actually changed.
// This is more important to implement now since this callback will disable all
// animations because we cannot rely on positions.
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
} else {
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
}
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
triggerUpdateProcessor();
}
}
void triggerUpdateProcessor() {
if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}```
RecycleView的setAdapter方法,可以看到里面調用了unregisterAdapterDataObserver及registerAdapterDataObserver方法進行取消注冊及注冊。

public void setAdapter(Adapter adapter) {
 // bail out if layout is frozen
 setLayoutFrozen(false);
 setAdapterInternal(adapter, false, true);
 requestLayout();
 }
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
 boolean removeAndRecycleViews) {
 if (mAdapter != null) {
 mAdapter.unregisterAdapterDataObserver(mObserver);
 mAdapter.onDetachedFromRecyclerView(this);
 }
 if (!compatibleWithPrevious || removeAndRecycleViews) {
 // end all running animations
 if (mItemAnimator != null) {
 mItemAnimator.endAnimations();
 }
 // Since animations are ended, mLayout.children should be equal to
 // recyclerView.children. This may not be true if item animator's end does not work as
 // expected. (e.g. not release children instantly). It is safer to use mLayout's child
 // count.
 if (mLayout != null) {
 mLayout.removeAndRecycleAllViews(mRecycler);
 mLayout.removeAndRecycleScrapInt(mRecycler);
 }
 // we should clear it here before adapters are swapped to ensure correct callbacks.
 mRecycler.clear();
 }
 mAdapterHelper.reset();
 final Adapter oldAdapter = mAdapter;
 mAdapter = adapter;
 if (adapter != null) {
 adapter.registerAdapterDataObserver(mObserver);
 adapter.onAttachedToRecyclerView(this);
 }
 if (mLayout != null) {
 mLayout.onAdapterChanged(oldAdapter, mAdapter);
 }
 mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
 mState.mStructureChanged = true;
 markKnownViewsInvalid();
 }```
到這里我們知道,setAdapter()方法會進行觀察者的注冊,當RecycleView的數據發送變化的時,調用了Adapter的notifyDataSetChange(),這個函數又會調用 AdapterDataObservable的notifyChanged();該函數會遍歷所有的觀察者的onChange函數,在 RecyclerViewDataObserver的onChange()函數中會獲取Adapter中數據集的新數量,然后調用RecycleView的requestLayout()方法重新進行布局,更新用戶的界面。這就是一個觀察者模式!
出處:http://huangjunbin.com/page/2/
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容