設計模式之觀察者模式

一配乱、什么叫觀察者?

先理解一個簡單的例子:很多購房者都在關(guān)注房子的價格變化于毙,每當房子的價格變化時歉糜,所有購房者都可以觀察得到,實際上購房者都屬于觀察者望众。

  • 觀察者模式的定義:定義對象間一種一對多的依賴關(guān)系,使得每當一個對象發(fā)生改變伞辛,則所有依賴她的對象都會等到通知并被自動更新烂翰。
  • 使用場景:1、 關(guān)聯(lián)行為場景;2蚤氏、事件多級觸發(fā)場景甘耿;3、跨系統(tǒng)的消息交互場景竿滨,如消息隊列佳恬、事件總線的處理機制

二、觀察者模式實現(xiàn)

在Java.util包中提供了Observable和Observer接口于游,使用它們即可完成觀察者模式毁葱。
Observable:需要被觀察的類必須繼承Observable類。Observable類的常用方法有:

  • 1贰剥、public void addObserver(Observer o) : 添加一個觀察者倾剿;
  • 2、public void deleteObserver(Observer o):刪除一個觀察者蚌成;
  • 3前痘、public void setChanged() ; 被觀察者狀態(tài)發(fā)生改變凛捏;
  • 4、public void notifyObservers(Object args) ; 通知所有觀察者狀態(tài)改變芹缔;

Observer:每個觀察者都需要實現(xiàn)Observer接口坯癣,Observer接口定義如下:

public interface Observer {
     /**
     *當被觀察者發(fā)生變化,并調(diào)用了notifyObserver方法最欠,就調(diào)用該方法
     * @param o   被觀察者
     * @param args  修改的內(nèi)容
     */
    void update(Observable o, Object arg);
}

例:

package sl.com.designmodedemo.observer;
import java.util.Observable;
import java.util.Observer;
/**
 * 定義房子為被觀察者
 */
class House extends Observable{
    private float price ;   //定義房子價格
    public House(float price) {
        this.price = price;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
        setChanged();           //通知變化點
        notifyObservers(price);      //通知所有觀察者價格改變
    }
    @Override
    public String toString() {
        return "房子的價格是:" + this.price;
    }
}
/**
 * 房子價格變化觀察者
 */
class HousePriceObserver implements Observer{
    private String name ;       //觀察房子價格變化的人名
    public HousePriceObserver(String name) {
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
            if (arg instanceof Float){              //判斷參數(shù)類型
                System.out.println(this.name + " 觀察到的價格更改為:" + ((float)arg));
            }
    }
}

/**
 * 測試
 */
public class Test {
    public static void main(String args[]){
        House house = new House(1000000.0F);
        HousePriceObserver observer1 = new HousePriceObserver("觀察者A");
        HousePriceObserver observer2 = new HousePriceObserver("觀察者B");
        HousePriceObserver observer3 = new HousePriceObserver("觀察者C");
        //加入觀察者
        house.addObserver(observer1);
        house.addObserver(observer2);
        house.addObserver(observer3);
        //輸出房子價格
        System.out.println(house);
        //修改房子價格
        house.setPrice(2000000.0F);
        System.out.println(house);
    }
}

程序運行結(jié)果為:

房子的價格是:1000000.0
觀察者C 觀察到的價格更改為:2000000.0
觀察者B 觀察到的價格更改為:2000000.0
觀察者A 觀察到的價格更改為:2000000.0
房子的價格是:2000000.0

從程序運行結(jié)果可以發(fā)現(xiàn)示罗,多個觀察者都在觀察著價格的變化,當被觀察者房子價格一變化窒所,則所有觀察者都會知道鹉勒;、

三吵取、Android 源碼分析

ListView是Android中重要控件之一禽额,而ListView有一個Adapter,通常我們在給ListView的數(shù)據(jù)進行操作變化后皮官,都會調(diào)用Adapter的notifyDataSetChanged()方法脯倒,這是為什么呢?下面我們從源碼解析:
第一步捺氢,跟進notifyDataSetChanged()方法藻丢,這個方法定義在android.widget的BaseAdapter中,具體代碼如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    //數(shù)據(jù)集觀察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();
    
    //代碼省略

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

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    //當數(shù)據(jù)集變化時摄乒,通知所有觀察者
   public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}

從這里應該看出來BaseAdapter就是一個觀察者模式悠反,那么BaseAdapter是如何運作的?這些觀察者又是什么馍佑?下面接著一步一步的分析斋否。
現(xiàn)在先到mDataSetObservalbe.notifyChanged()的源碼看看:

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
    * 調(diào)用每個觀察者的onChanged函數(shù)來通知他們被觀察者發(fā)生了變化
    */
   public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    //代碼省略
}

這個代碼很簡單,就是在mDataSetObservable.notifyChanged()中遍歷所有的觀察者拭荤,并且調(diào)用他的onChanged()方法茵臭,從而告知觀察者發(fā)生了變化。
那么 這些觀察者從哪里來的呢舅世?其實這些觀察者是就是ListView在setAdapter方法設置Adapter產(chǎn)生的旦委,我們看相關(guān)代碼:

    @Override
    public void setAdapter(ListAdapter adapter) {
        //如果已經(jīng)有了一個Adapter,并且mDataSetObserver不為空
        if (mAdapter != null && mDataSetObserver != null) {
            //先注銷該Adapter的觀察者
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        //代碼省略
       
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            //獲取數(shù)據(jù)的數(shù)量
            mItemCount = mAdapter.getCount();
            checkFocus();
           //注意這里:創(chuàng)建了一個數(shù)據(jù)觀察者
            mDataSetObserver = new AdapterDataSetObserver();
            //將這個觀察者注冊到Adapter中雏亚,實際上是注冊到DataSetObservable中
            mAdapter.registerDataSetObserver(mDataSetObserver);
            //代碼省略 
        } else {
           //代碼省略
         }
        requestLayout();
    }

從程序可以看到缨硝,在設置Adapter的時會構(gòu)建一個AdapterDataSetObserver,這就是上面說的觀察者评凝,最后將這個觀察者注冊到了Adapter追葡,這樣我們的被觀察者、觀察者就都有了;
那么AdapterDataSetObserver是什么宜肉?它如何運作匀钧?那么現(xiàn)在來看看,AdapterDataObserver定義在ListView的父類AbsListView中谬返,具體代碼如下:

 class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
    }

它又繼承自AbsListView的父類AdapterView的AdapterDataSetObserver之斯,具體代碼如下:

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;

    //調(diào)用Adapter的notifyDataSetChanged的時會調(diào)用所有觀察者的onChanged()方法,核心就是這里
      @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();
        }

      //代碼省略

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

到這里就知道了遣铝,當ListView的數(shù)據(jù)發(fā)生變化時佑刷,調(diào)用Adapter的notifyDataSetChanged(),這個方法又調(diào)用了DataSetObservable的notifyChanged()方法,這個方法會調(diào)用所有觀察者AdapterDataSetObserver的onChanged(),在onChanged函數(shù)中又會調(diào)用ListView重新布局的函數(shù)和使得ListView刷新界面酿炸,這就是一個觀察者模式瘫絮;
現(xiàn)在再整理一下這個過程,AdapterView中又一個內(nèi)部類AdapterDataSetObserver,在ListView設置Adapter時會構(gòu)建一個AdapterDataSetObserver填硕,并且注冊到Adapter中麦萤,這就是一個觀察者。而Adapter中有一個數(shù)據(jù)集被觀察者DataSetObservable扁眯,在數(shù)據(jù)數(shù)量發(fā)生變更時壮莹,開發(fā)者手動調(diào)用Adapter.notifyDataSetChanged(),而notifyDataSetChanged()實際上是調(diào)用DataObservable的notifyChanged()方法,該方法會遍歷所有的觀察者的onChanged()姻檀。在AdapterDataSetObserver的onChanged()會獲取Adpaer中數(shù)據(jù)集的新數(shù)量命满,然后調(diào)用ListView的requestLayout()方法重新進行布局,更新用戶界面绣版。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胶台,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杂抽,更是在濱河造成了極大的恐慌概作,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件默怨,死亡現(xiàn)場離奇詭異,居然都是意外死亡骤素,警方通過查閱死者的電腦和手機匙睹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來济竹,“玉大人痕檬,你說我怎么就攤上這事∷妥牵” “怎么了梦谜?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我唁桩,道長闭树,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任荒澡,我火速辦了婚禮报辱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘单山。我一直安慰自己碍现,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布米奸。 她就那樣靜靜地躺著昼接,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悴晰。 梳的紋絲不亂的頭發(fā)上慢睡,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音膨疏,去河邊找鬼一睁。 笑死,一個胖子當著我的面吹牛佃却,可吹牛的內(nèi)容都是我干的者吁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼饲帅,長吁一口氣:“原來是場噩夢啊……” “哼复凳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起灶泵,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤育八,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赦邻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體髓棋,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年惶洲,在試婚紗的時候發(fā)現(xiàn)自己被綠了按声。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡恬吕,死狀恐怖签则,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铐料,我是刑警寧澤渐裂,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布豺旬,位于F島的核電站,受9級特大地震影響柒凉,放射性物質(zhì)發(fā)生泄漏族阅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一扛拨、第九天 我趴在偏房一處隱蔽的房頂上張望耘分。 院中可真熱鬧,春花似錦绑警、人聲如沸求泰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渴频。三九已至,卻和暖如春北启,著一層夾襖步出監(jiān)牢的瞬間卜朗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工咕村, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留场钉,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓懈涛,卻偏偏與公主長得像逛万,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子批钠,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 架構(gòu)師之路 目錄 1宇植、觀察者模式概念 1.1 介紹 當對象間存在一對多關(guān)系時,則使用觀察者模式(O...
    香沙小熊閱讀 1,265評論 1 2
  • 一埋心、觀察者模式的定義 定義對象間一對多的依賴關(guān)系指郁,使得當前對象改變了狀態(tài),則所有依賴于它的對象都會得到通知并自動更...
    sssssss_閱讀 576評論 0 0
  • 參考 《設計模式:可復用面向?qū)ο筌浖幕A 》5.7 Observer 觀察者 對象行為型模式 《設計模式解析》 ...
    WangGavin閱讀 515評論 0 2
  • 1 定義 定義對象之間的一種一對多依賴關(guān)系拷呆,使得每當一個對象狀態(tài)發(fā)生改變時闲坎,其相關(guān)依賴對象皆得到通知并被自動更新。...
    菜小軒526閱讀 541評論 3 3
  • 有時候茬斧,我還是會想起若曦箫柳。她現(xiàn)在會是什么樣子?一定還是那個樣子啥供。她就像是那種不會被歲月變老的女孩。 “我是妖精變的...
    又見光明閱讀 655評論 1 2