設計模式之觀察者模式源碼分析及實踐
今日科技快訊
4月27日,滴滴出行與國際金融公司(IFC)簽署合作諒解備忘錄,將在新興市場中開啟女性發展、數字普惠金融、綠色金融及跨國合作等領域的合作。滴滴表示,在這一合作框架下,雙方將探索利用數字技術實現賦能女性乘客、司機、女性中小型企業主及女性員工。
作者簡介
咦!今天好像是周六哦!接下來的三天就不再發文章了,這裡提前祝大家五一節快樂!
本篇來自Marker_Sky的投稿,分享了觀察者模式 源碼分析和在 RecyclerView 中的體現,一起來看看!希望大家喜歡。
Marker_Sky的博客地址:
https://www.jianshu.com/u/d46b4a47db84
前言
最近在學習 EventBus 和 RxJava 相關知識,了解到兩者都使用了觀察者模式。以前對該模式只有一個模糊的概念,導致看 E 和 R 時一知半解,所以下定決心把這個模式仔細回顧和記錄一下。
正文
觀察者模式相關概念定義:一個對象和多個形成依賴關係,當一個對象改變狀態,所以依賴於它的對象都會得到通知並執行相應邏輯。
簡單理解:A 是被觀察者,B、C、D 是觀察者。當 A 發生變化時發出通知告知 B、C、D,然後 B、C、D 可根據 A 發送的數據來做具體的事情。
重要作用:解耦。也就是將觀察者和被觀察者解耦。
解耦的好處不用多說,設計模式中 單一職責原則 也表明我們希望每個實體(類、函數、模塊等)能引起其改變的原因只有它自己。
也就是說,我們希望觀察者的邏輯和被觀察者的邏輯是分離的。
使用場景
某些時候需要一些對象有所關聯,而且這種關聯是可以拆分而不是組合在一起的。
某一事件發生,需要向多方觸發。
消息交換,如消息隊列、事件匯流排的處理機制。
UML類圖
觀察者模式主要角色
Subject:抽象主題,被觀察者(Observable)的抽象,管理著眾多觀察者的實現,可以實現添加或刪除觀察者的功能。
ConcreteSubject:具體主題,被觀察者(Observable)的實現,通過該實現來向觀察者發送通知。
Observer:抽象觀察者(Observer),觀察者的抽象。一般是介面,實現該介面生成各種各樣的觀察者。
ConcreteObserver:具體觀察者,抽象觀察者的具體實現,當被觀察者發生變化時執行具體邏輯。
概念理解起來確實比較麻煩,但是結合具體的例子來理解的話會好很多,下面就是一個簡單的例子。
觀察者模式簡單實現
要實現的功能:定義一個數據源,當數據源發生變化時,其它所有觀察者收到消息並更新 UI。
具體步驟
定義一個數據源 DataSource,繼承 Observable。Observable 屬於觀察者模式中的抽象主題(Subject),那麼這個實現類 DataSource 就是具體主題(ConcreteSubject)了。
這個實現類的具體作用是作為被觀察者,當它的數據發生變化時去通知所有觀察者。
首先定義一個更新數據的方法 updateData(),參數是可以被觀察者接收的任意類型。
這裡定義的是 String 類型的,因為我的觀察者是自定義 TextView,需要 String 來更新數據。setChanged() 這個是父類 Observable 中的方法,用來改變一個標記,表明數據已經更新。後面會根據該標記來決定是否通知所有觀察者。notifyObservers(content)父類中的方法,該方法會調用所有觀察者的 update() 方法實現數據的傳遞更新。
定義觀察者 MyTextView,這是我自己定義的 TextView,實現了 Observer 介面。
Observer 介面就是抽象觀察者了,那麼這裡的實現類 MyTextView 就是具體觀察者角色(ConcreteObserver)了。
自定義 TextView,三個構造函數不多說。update(Observable o, Object arg) 方法是介面 Observer 中的方法,參數 Observable o 是具體的觀察者類,Object arg是具體的數據。
定義 ObserverTestActivity,布局中寫個 Button 模擬數據發生變化。接著使用剛才自定義的 MyTextView,這裡寫了兩個。
模擬數據變化並通知 UI。
要注意的是只有把所有被觀察者使用 addObserver() 添加進來才會收到後續數據更新。
效果
當我點擊按鈕時,兩個 TextView 的數據都發生了變化。
源碼分析
結合上文例子分析 java.util 包下的 Observer 和 Observable 的實現。
介面 Observer
很簡單的一個介面,只定義了一個 update() 方法,參數 Observable 和 Object。
Observable o 表示具體的被觀察者對象,Object arg 是要傳遞和更新的數據。
上文 MyTextView 實現該介面,並實現 update() 方法。該方法的作用是當 DataSource 發生數據變化時,會遍歷所有已添加的 MyTextView 並調用它們的 update() 方法來更新 UI。
類 Observable
該類比較簡單,按注釋順序來看:
兩個變數 changed 和 obs。changed是數據是否變化的標記,主要作用是避免重複通知觀察者。obs是一個自增長的數組,用來存放具體的觀察者對象,也就是實現了 Observer 的實現類。
這裡有一個知識點:設計模式的 依賴倒置原則:面向介面編程而不是面向具體對象編程。這個 obs 數組存放的是介面類型而不是某個具體實現類型表明了這一點。
添加和刪除觀察者對象的方法就是往數組 obs 添加和刪除元素。
notifyObservers() 通知所有觀察者,可以看到該函數使用了 synchronized 保證線程安全,然後 hasChanged() 獲取當前標記狀態,如果標記為 false 則 return 不進行通知。
那麼這個標記什麼時候才能為 true 呢?是在該類的子類中,也就是剛才例子中 DataSource 數據源類 updateData() 方法中首先調用 setChanged() 來設置標記為 true,表明數據即將進行更新。
之後將數組 obs 轉化為 Object 數組,設置標記為 false,然後遍歷 obs 數組並調用所有元素的 update() 方法。也就是例子中所有添加進來的 MyTextView 對象的 update() 方法都會被調用,所有 MyTextView 的 text 發生變化。
到這裡 Observer(抽象觀察者) 和 Observable(抽象被觀察者)已經分析的差不多了,那麼再結合剛才的例子來總結一下:
MyTextView 實現 Observer,是具體的觀察者。實現方法 update() 來執行具體的邏輯。
DataSource 繼承 Observable,是具體的被觀察者。通過 notifyObservers() 方法傳遞具體數據來實現通知所有觀察者,傳遞的具體邏輯是在父類中實現的。
Observable 作為抽象被觀察者實現具體的數據傳遞邏輯,其內部維護了一個標記和具體觀察者列表,當數據發生變化時遍歷該列表並通過調用 update() 方法來通知具體觀察者實現不同的邏輯。
觀察者模式在 RecyclerView 中的體現
通過以上對觀察者模式的了解,四個主要角色 抽象觀察者、具體觀察者、抽象被觀察者和具體觀察者 起到了重要作用,而他們的運作是通過三個重要方法 註冊觀察者、解除註冊、發送通知,那麼分析 RecyclerView 也要從這幾點入手。
首先我們知道 RecyclerView 中使用了觀察者模式(和 ListView 類似),也知道 notifyDataSetChanged() 起到通知界面刷新的功能,那麼就從這個方法入手。
RecyclerView.Adapter # notifyDataSetChanged()
這個方法其實是在 RecyclerView 的內部靜態抽象類 Adapter 中,可以看到 notifyDataSetChanged() 實際調用了 Adapter 的成員變數 mObservable 的 notifyChanged() 方法,該方法的實現暫且不管。這個變數 mObservable 命名方式很熟悉,那麼就去看一下這個 AdapterDataObservable 類吧。
這個類繼承了 Observable,看樣子是一個被觀察者實現類,為了驗證一下,去看看 Observable 類。
類Observable
我特意把包名也貼出來了,它不是 java 里的 Observable,而是 android 自己的實現。protected final ArrayList mObservers = new ArrayList();:根據這個 ArrayList 可以看出該類維護了一個類列表,那麼這類應該就是具體觀察者了。registerObserver()、unregisterObserver()、unregisterAll() 符合抽象被觀察者管理具體觀察者的邏輯,所以這個類就是抽象被觀察者角色了。
這樣就可以確定剛才 RecyclerView.AdapterDataObservable 是一個具體被觀察者了。理一下現在得到的邏輯:
RecyclerView 靜態內部類 AdapterDataObservable 是具體被觀察者,Observable 類是 抽象被觀察者,他們管理著具體觀察者。Adapter 中持有具體被觀察者實例 mObservable。我們調用 notifyDataSetChanged() 實際調用了 具體被觀察者實例 mObservable 的 notifyChanged() 方法。
那麼接下來分析一下 mObservable.notifyChanged(); 幹了什麼:
AdapterDataObservable # notifyChanged()
這個方法做的事情很簡單,遍歷 mObservers,獲取其中的具體觀察者並調用他們的 onChanged() 方法達到發送通知的目的。這個 mObservers 其實是一個 ArrayList,是由抽象被觀察者(Observable)管理的列表,在剛才的 Observable 類中早就定義好了。
在這裡找到了發送通知的方法,遍歷所有的具體觀察者並調用他們的 onChanged() 方法,那麼再看一下 onChanged() 方法的實現:
可以看到是抽象類的空實現,那麼就去找它的實現類,Ctrl+F 搜索 "extends AdapterDataObserver"。歐耶找到了,這就說明實現類也定義本類中。其實大多數實現類並不在本類中,這個小技巧也就是偶爾取巧用得上(#滑稽)。
類 RecyclerViewDataObserver
可以看到重寫的 onChanged() 中調用了 requestLayout() 申請重新布局,至於怎樣實現,感興趣的同學可以參考我另一篇文章:
Android View:一條線理解 View 繪製流程:
https://www.jianshu.com/p/e9ce78a01a93
那麼回到正題,RecyclerViewDataObserver 實現了抽象類 AdapterDataObserver 的方法並被具體被觀察者調用通知。那麼可以得出結論:AdapterDataObserver 是抽象觀察者,抽象出各種通知方法。RecyclerViewDataObserver 是具體觀察者,實現抽象觀察者中的方法執行具體邏輯。
這樣四個角色都找齊了,三個方法只找到了發送通知的。還差註冊和解除觀察者的方法,這兩個方法在 setAdapter 中所體現:
RecyclerView # setAdapter()
註冊和解除觀察者實際是在 setAdapterInternal() 中:
RecyclerView # setAdapterInternal()
上面的代碼主要關注的是下面兩點:
第一個 if 中如果 mAdapter 不為 null,也就是重新設置 RecyclerView 的 adapter 時先解除舊的觀察者對象,為新的 adapter 註冊觀察者。
注意這裡傳遞的參數 mObserver 是 RecyclerViewDataObserver 類型的,這個類是具體觀察者。
mAdapter.unregisterAdapterDataObserver 舊的 adapter 解除註冊,adapter.registerAdapterDataObserver(mObserver); 新的 adapter 註冊具體觀察者。
Adapter # registerAdapterDataObserver()、unregisterAdapterDataObserver()
可以看到解除和註冊的方法是通過 Adapter 中保存的具體被觀察者對象實例(mObservable)調用父類抽象被觀察者(Observable)中的註冊和解除觀察者來完成的。先看傳遞過來的參數類型是 AdapterDataObserver,也就是我們的抽象觀察者,也就說明了抽象被觀察者中維護的 ArrayList mObservers 的這個 就是 AdapterDataObserver。而這個 ArrayList 具體保存的是 AdapterDataObserver 的實現類 RecyclerViewDataObserver。
這一塊我表達的可能不夠清晰,那麼這裡的邏輯再理清一下:
在 setAdapter() 的過程中,將具體觀察者類的實例 mObserver 作為參數傳遞,用於設置具體的觀察者。然後調用 Adapter 中的 registerAdapterDataObserver(AdapterDataObserver observer) 方法進行註冊,因為 mObserver 是 AdapterDataObserver 的子類實例,所以可以接收。接著再通過 Adapter 中的 具體被觀察者實例 mObservable 調用父類 Observable 中的註冊和解除註冊方法處理邏輯。到這裡就差不多完成了整個過程:
抽象被觀察者:Observable
具體被觀察者:AdapterDataObservable
抽象觀察者:AdapterDataObserver
具體觀察者:RecyclerViewDataObserver
註冊:registerAdapterDataObserver()
解除註冊:unregisterAdapterDataObserver()
發送消息:notifyDataSetChanged() --> mObservable.notifyChanged() --> mObservers.get(i).onChanged()
觀察者模式其它實現
觀察者模式除了上文 java 包中的實現外,還有其它實現方式。話說回來,只要大方向不錯,根據實際需求去定製設計模式和代碼或許更好。
這裡記錄一下百度百科中記錄的觀察者模式 Demo。
首先是抽象主題,被觀察者抽象:
從上面代碼可以看出:
ArrayList> obserList:觀察者列表,Observable 維護著所有抽象觀察者。
registerObserver()、unRegisterObserver():Observable 包含添加和移除觀察者對象的方法。
notifyObservers():通知所有觀察者的函數,可以自己實現來統一管理,也可以交給子類去實現。這裡是抽象方法,由子類實現。
抽象觀察者,定義更新方法
具體被觀察者,繼承 Observable 類。
notifyObserver():實現父類抽象方法 notifyObserver(),遍歷所有觀察者,通過 java 反射機制調用具體觀察中類的方法。
具體觀察者類,需要添加到抽象被觀察者維護的列表中。
總結
綜上,只有找到四個重要角色:抽象觀察者、具體觀察者、抽象被觀察者和具體觀察者,以及三個方法:註冊、解除註冊和發送通知,就可以理清觀察者模式的應用了。
觀察者模式的優點
解耦
觀察者和被觀察者之間建立的是一個抽象的耦合,被觀察者並不需要了解每一個具體的觀察者,但是可以通過共同的介面來控制它們。
擴展性高
如果需要創建新的觀察者,只需要實現觀察者抽象介面即可。
觀察者模式的缺點
效率問題。如果觀察者數量過多,遍歷並按順序執行它們的方法會產生效率問題。或者某個觀察者所執行的代碼過於繁瑣,影響其他觀察者信息或數據的接收。
有關觀察者模式的記錄就先到這裡,在學會這些知識後靈活運用才是最重要的。個人覺得沒有最好的代碼,只有最合適的代碼。
或者掃一掃關注我的公眾號


TAG:郭霖 |