Android的設(shè)計模式-觀察者模式

前言

Android的設(shè)計模式系列文章介紹,歡迎關(guān)注,持續(xù)更新中:

Android的設(shè)計模式-設(shè)計模式的六大原則
一句話總結(jié)23種設(shè)計模式則
創(chuàng)建型模式:
Android的設(shè)計模式-單例模式
Android的設(shè)計模式-建造者模式
Android的設(shè)計模式-工廠方法模式
Android的設(shè)計模式-簡單工廠模式
Android的設(shè)計模式-抽象工廠模式
Android的設(shè)計模式-原型模式
行為型模式:
Android的設(shè)計模式-策略模式
Android的設(shè)計模式-狀態(tài)模式
Android的設(shè)計模式-責(zé)任鏈模式
Android的設(shè)計模式-觀察者模式
Android的設(shè)計模式-模板方法模式
Android的設(shè)計模式-迭代器模式
Android的設(shè)計模式-備忘錄模式
Android的設(shè)計模式-訪問者模式
Android的設(shè)計模式-中介者模式
Android的設(shè)計模式-解釋器模式
Android的設(shè)計模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計模式-代理模式
Android的設(shè)計模式-組合模式
Android的設(shè)計模式-適配器模式
Android的設(shè)計模式-裝飾者模式
Android的設(shè)計模式-享元模式
Android的設(shè)計模式-外觀模式
Android的設(shè)計模式-橋接模式

1.定義

定義對象間的一種一個對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)送改變時,所以依賴于它的對象都得到通知并被自動更新。

2.介紹

  • 觀察者屬于行為型模式。
  • 觀察者模式又被稱作發(fā)布/訂閱模式。
  • 觀察者模式主要用來解耦,將被觀察者和觀察者解耦,讓他們之間沒有沒有依賴或者依賴關(guān)系很小。

3.UML類圖

觀察者模式UML類圖 .jpg
角色說明:
  • Subject(抽象主題):又叫抽象被觀察者,把所有觀察者對象的引用保存到一個集合里,每個主題都可以有任何數(shù)量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
  • ConcreteSubject(具體主題):又叫具體被觀察者,將有關(guān)狀態(tài)存入具體觀察者對象;在具體主題內(nèi)部狀態(tài)改變時,給所有登記過的觀察者發(fā)出通知。
  • Observer (抽象觀察者):為所有的具體觀察者定義一個接口,在得到主題通知時更新自己。
  • ConcrereObserver(具體觀察者):實現(xiàn)抽象觀察者定義的更新接口,當(dāng)?shù)玫街黝}更改通知時更新自身的狀態(tài)。

4.實現(xiàn)

繼續(xù)以送快遞為例,快遞員有時只是把快遞拉到樓下,然后就通知收件人下樓去取快遞。

4.1 創(chuàng)建抽象觀察者

定義一個接到通知的更新方法,即收件人收到通知后的反應(yīng):

    public interface Observer {//抽象觀察者
        public void update(String message);//更新方法
    }
4.2 創(chuàng)建具體觀察者

實現(xiàn)抽象觀察者中的方法,這里創(chuàng)建兩個類,一個男孩類和一個女孩類,定義他們收到通知后的反應(yīng):

    public class Boy implements Observer {

        private String name;//名字
        public Boy(String name) {
            this.name = name;
        }
        @Override
        public void update(String message) {//男孩的具體反應(yīng)
            System.out.println(name + ",收到了信息:" + message+"屁顛顛的去取快遞.");
        }
    }

    public class Girl implements Observer {

        private String name;//名字
        public Girl(String name) {
            this.name = name;
        }
        @Override
        public void update(String message) {//女孩的具體反應(yīng)
            System.out.println(name + ",收到了信息:" + message+"讓男朋友去取快遞~");
        }
    }
4.3 創(chuàng)建抽象主題

即抽象被觀察者,定義添加,刪除,通知等方法:

    public interface  Observable {//抽象被觀察者
         void add(Observer observer);//添加觀察者
 
         void remove(Observer observer);//刪除觀察者
    
         void notify(String message);//通知觀察者
    }
4.4 創(chuàng)建具體主題

即具體被觀察者,也就是快遞員,派送快遞時根據(jù)快遞信息來通知收件人讓其來取件:

    public class Postman implements  Observable{//快遞員
        
        private List<Observer> personList = new ArrayList<Observer>();//保存收件人(觀察者)的信息
        @Override
        public void add(Observer observer) {//添加收件人
            personList.add(observer);
        }

        @Override
        public void remove(Observer observer) {//移除收件人
            personList.remove(observer);

        }

        @Override
        public void notify(String message) {//逐一通知收件人(觀察者)
            for (Observer observer : personList) {
                observer.update(message);
            }
        }
    }
4.5 客戶端測試
    public void test(){
        Observable postman=new Postman();
        
        Observer boy1=new Boy("路飛");
        Observer boy2=new Boy("喬巴");
        Observer girl1=new Girl("娜美");

        postman.add(boy1);
        postman.add(boy2);
        postman.add(girl1);
        
        postman.notify("快遞到了,請下樓領(lǐng)取.");
    }
輸出結(jié)果:
路飛,收到了信息:快遞到了,請下樓領(lǐng)取.屁顛顛的去取快遞.
喬巴,收到了信息:快遞到了,請下樓領(lǐng)取.屁顛顛的去取快遞.
娜美,收到了信息:快遞到了,請下樓領(lǐng)取.讓男朋友去取快遞~
4.6 說明:

實際上,JDK內(nèi)部也內(nèi)置了Observable(抽象被觀察者),Observer(抽象觀察者)這兩個類,我們也可以直接拿來用,其代碼如下:

public interface Observer {//(抽象觀察者
    //只定義了一個update方法
    void update(Observable o, Object arg);
}

public class Observable {//抽象被觀察者
    private boolean changed = false;//定義改變狀態(tài),默認(rèn)為false
    private final ArrayList<Observer> observers;//定義一個觀察者list

    public Observable() {//構(gòu)造函數(shù),初始化一個觀察者list來保存觀察者
        observers = new ArrayList<>();
    }
    //添加觀察者,帶同步字段的,所以是線程安全的
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    //刪除觀察者
    public synchronized void deleteObserver(Observer o) {
        observers.remove(o);
    }

    //通知所以觀察者,無參數(shù)
    public void notifyObservers() {
        notifyObservers(null);
    }

     //通知所有觀察者,帶參數(shù)
    public void notifyObservers(Object arg) {

        Observer[] arrLocal;
        //加synchronized字段,保證多線程下操作沒有問題
        synchronized (this) {
            if (!hasChanged())//這里做了是否發(fā)生改變的判斷,是為了防止出現(xiàn)無意義的更新
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);//ArrayList轉(zhuǎn)換成一個臨時的數(shù)組,這樣就防止了通知,添加,移除同時發(fā)生可能導(dǎo)致的異常
            clearChanged();///清除改變狀態(tài),設(shè)置為false
        }
        //遍歷逐一通知
        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

    //清楚所有觀察者
    public synchronized void deleteObservers() {
        observers.clear();
    }

    //設(shè)置被觀察者為改變狀態(tài),設(shè)置為true
    protected synchronized void setChanged() {
        changed = true;
    }

    //清除改變狀態(tài),設(shè)置為false
    protected synchronized void clearChanged() {
        changed = false;
    }

    //返回當(dāng)前的改變狀態(tài)
    public synchronized boolean hasChanged() {
        return changed;
    }

    //觀察者數(shù)量
    public synchronized int countObservers() {
        return observers.size();
    }
}

5. 應(yīng)用場景

  • 當(dāng)一個對象的改變需要通知其它對象改變時,而且它不知道具體有多少個對象有待改變時。
  • 當(dāng)一個對象必須通知其它對象,而它又不能假定其它對象是誰
  • 跨系統(tǒng)的消息交換場景,如消息隊列、事件總線的處理機制。

6. 優(yōu)點

  • 解除觀察者與主題之間的耦合。讓耦合的雙方都依賴于抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。
  • 易于擴展,對同一主題新增觀察者時無需修改原有代碼。

7. 缺點

  • 依賴關(guān)系并未完全解除,抽象主題仍然依賴抽象觀察者。
  • 使用觀察者模式時需要考慮一下開發(fā)效率和運行效率的問題,程序中包括一個被觀察者、多個觀察者,開發(fā)、調(diào)試等內(nèi)容會比較復(fù)雜,而且在Java中消息的通知一般是順序執(zhí)行,那么一個觀察者卡頓,會影響整體的執(zhí)行效率,在這種情況下,一般會采用異步實現(xiàn)。
  • 可能會引起多余的數(shù)據(jù)通知。

8. Android中的源碼分析

8.1 控件中Listener監(jiān)聽方式

Android中我們遇到的最常用的觀察者模式就是各種控件的監(jiān)聽,如下:

        Button button = (Button) findViewById(R.id.button);
        //注冊觀察者
        button.setOnClickListener(new View.OnClickListener() {
            //觀察者實現(xiàn)
            @Override
            public void onClick(View arg0) {
                Log.d("test", "Click button ");
            }
        });

上面代碼中,button就是具體的主題,也就是被觀察者;new出來的View.OnClickListenerd對象就是具體的觀察者;OnClickListener實際上就是個接口,也就是抽象觀察者;通過setOnClickListener把觀察者注冊到被觀察者中。

一旦button捕獲的點擊事件,即狀態(tài)發(fā)生變化的時候,就會通過回調(diào)注冊的OnClickListener觀察者的onClick方法會來通知觀察者,Button狀態(tài)發(fā)生變化。。

相關(guān)源碼分析:

    public interface OnClickListener {//抽象觀察者

        void onClick(View v);//只有onClick這個方法
    }

    //注冊觀察者
    public void setOnClickListener(@Nullable View.OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);//設(shè)置為可點擊
        }
        getListenerInfo().mOnClickListener = l;//把傳入的 OnClickListener 對象賦值給了 getListenerInfo().mOnClickListener,即mListenerInfo的mOnClickListener持有OnClickListener對象的引用
    }

    ListenerInfo getListenerInfo() {//返回ListenerInfo對象,這里是一個單例模式
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

    public boolean performClick() {//執(zhí)行點擊事件
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);//執(zhí)行onClick方法,li.mOnClickListener即OnClickListener對象
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

8.2 Adapter的notifyDataSetChanged()方法

當(dāng)我們使用ListView時,需要更新數(shù)據(jù)時我們就會調(diào)用AdapternotifyDataSetChanged()方法,那么我們來看看notifyDataSetChanged()的實現(xiàn)原理,這個方法是定義在BaseAdaper中,具體代碼如下:

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就是具體的被觀察者。接下來看看 mDataSetObservable.notifyChanged()的實現(xiàn):

//數(shù)據(jù)集被觀察者
public class DataSetObservable extends Observable<DataSetObserver> {
   
    public void notifyChanged() {
        synchronized(mObservers) {
            //遍歷所有觀察者,并調(diào)用他們的onChanged()方法
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    //其他代碼略
}

現(xiàn)在我們看到了有觀察者的影子,那么這些觀察者是從哪里來的呢?實際上這些觀察者是在ListView通過setAdaper()設(shè)置Adaper時產(chǎn)生的:

public class ListView extends AbsListView {
    //其他代碼略
    
    public void setAdapter(ListAdapter adapter) {
        //如果已存在Adapter,先注銷該Adapter的觀察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }
        
        //其他代碼略
        
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();//獲取Adapter中的數(shù)據(jù)的數(shù)量
            checkFocus();

            mDataSetObserver = new AdapterDataSetObserver();//創(chuàng)建一個數(shù)據(jù)集觀察者
            mAdapter.registerDataSetObserver(mDataSetObserver);//注冊觀察者

           //其他代碼略
        } 
    }
}

從上面的代碼可以看到,觀察者有了,那么這個觀察者主要是干什么的呢?

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();//調(diào)用父類的onChanged()方法
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }

       //其他代碼略
    }

AdapterDataSetObserver類中的onChanged()方法沒看出啥,繼續(xù)看他父類的onChanged()方法:

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;
        //觀察者的核心實現(xiàn)
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();//獲取Adapter中的數(shù)據(jù)的數(shù)量
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //重新布局
            requestLayout();
        }

       //其他代碼略
    }

最終就是在AdapterDataSetObserver這個類里面的onChanged()方法中實現(xiàn)了布局的更新。

簡單總結(jié):

  • 當(dāng)ListView的數(shù)據(jù)發(fā)生變化時,我們調(diào)用AdapternotifyDataSetChanged()方法,這個方法又會調(diào)用所有觀察者(AdapterDataSetObserver)的onChanged()方法,onChanged()方法又會調(diào)用requestLayout()方法來重新進(jìn)行布局。

8.3 BroadcastReceiver

BroadcastReceiver作為Android的四大組件之一,實際上也是一個典型的觀察者模式.通過sendBroadcast發(fā)送廣播時,只有注冊了相應(yīng)的IntentFilterBroadcastReceiver對象才會收到這個廣播信息,其onReceive方法才會被調(diào)起.BroadcastReceiver的代碼比較復(fù)雜,這里就不展開了.先挖個坑,后面也會出BroadcastReceiver的相關(guān)源碼分析.

8.4 其他

另外,一些著名的第三方事件總線庫,比如RxJava、RxAndroid、EventBus、otto等等,也是使用了觀察者模式.有興趣的可以去看下他們的源碼.

相關(guān)文章閱讀
Android的設(shè)計模式-設(shè)計模式的六大原則
一句話總結(jié)23種設(shè)計模式則
創(chuàng)建型模式:
Android的設(shè)計模式-單例模式
Android的設(shè)計模式-建造者模式
Android的設(shè)計模式-工廠方法模式
Android的設(shè)計模式-簡單工廠模式
Android的設(shè)計模式-抽象工廠模式
Android的設(shè)計模式-原型模式
行為型模式:
Android的設(shè)計模式-策略模式
Android的設(shè)計模式-狀態(tài)模式
Android的設(shè)計模式-責(zé)任鏈模式
Android的設(shè)計模式-觀察者模式
Android的設(shè)計模式-模板方法模式
Android的設(shè)計模式-迭代器模式
Android的設(shè)計模式-備忘錄模式
Android的設(shè)計模式-訪問者模式
Android的設(shè)計模式-中介者模式
Android的設(shè)計模式-解釋器模式
Android的設(shè)計模式-命令模式
結(jié)構(gòu)型模式:
Android的設(shè)計模式-代理模式
Android的設(shè)計模式-組合模式
Android的設(shè)計模式-適配器模式
Android的設(shè)計模式-裝飾者模式
Android的設(shè)計模式-享元模式
Android的設(shè)計模式-外觀模式
Android的設(shè)計模式-橋接模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容