參考
- 《設計模式:可復用面向?qū)ο筌浖幕A(chǔ) 》5.7 Observer 觀察者 對象行為型模式
- 《設計模式解析》 18.4 Observer模式
- 《Android源碼設計模式解析與實戰(zhàn)》第12章 解決,解耦的鑰匙--觀察者模式
本人能力有限,如有明顯錯誤,不規(guī)范的地方,請指正,謝謝。
意圖
定義對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并自動更新。
別名
依賴(Dependents)發(fā)布-訂閱(Publish-Subscribe)
適用場景
- 當一個抽象模型有兩個方面,其中一個方面依賴于另一個方面。將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用。
- 當對一個對象的改變需要同時改變其它對象,而不知道具體有多少對象有待改變。
- 當一個對象必須通知其它對象,而它又不能確定其它對象是誰。
結(jié)構(gòu)

- subject 抽象主題,也就是被觀察的角色,抽象主題角色把所有觀察者對象的引用保存在一個集合里,每個主題都可以由任意數(shù)量的觀察者,抽象主題提供一個接口,可以增加和刪除觀察者對象。
- ConcreteSubject 具體主題,也就是具體被觀察者角色
- Observer 抽象觀察者,他定義了一個更新接口,使得訂閱的主題更改時更新到自己。
- ConcreteObserver 具體觀察者
優(yōu)點
- 觀察者與被觀察者之間是抽象耦合,應對業(yè)務變化。
- 增強系統(tǒng)靈活性,可擴展性。
缺點
在應用觀察者模式時需要考慮一些開發(fā)效率和運行效率問題,程序中包括一個被觀察者,多個觀察則,開發(fā)和調(diào)式等內(nèi)容會比較復雜。
例子
Observer模式是直接接觸過的最常見的設計模式之一,GUI程序應用得比較廣。
例子1 程序員訂閱Android博客周刊
描述
開發(fā)技術(shù)前線網(wǎng)站是一個匯集各種技術(shù)文章的網(wǎng)站,它支持郵箱訂閱,一旦有用戶訂閱了它,每當網(wǎng)站出新內(nèi)容時,會自動將新內(nèi)容推送到用戶郵箱。
因為java核心庫里已經(jīng)有了Observer抽象觀察者接口和Observable抽象被觀察者類,所以我們直接實現(xiàn)和繼承它們即可擴展我們自己的業(yè)務。
簡單代碼實現(xiàn)
/**
* 程序員是訂閱者,就是具體的觀察者
*
* @author newtrekWang
* @email wangjiaxing20160101@gmail.com
* @time 2018/8/24 0:07
*/
public class Coder implements Observer {
/**
* 名字
*/
private String name;
public Coder(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
if (arg instanceof Page) {
System.out.println(name + " 得到了文章:" + arg.toString());
}
}
@Override
public String toString() {
return "Coder{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 開發(fā)技術(shù)網(wǎng)站
* @author newtrekWang
* @email wangjiaxing20160101@gmail.com
* @time 2018/8/24 0:16
*/
public class DevTechFrontier extends Observable {
/**
* 通知所有觀察者
* @param page 新的文章
*/
public void postNewPage(Page page){
// 設置狀態(tài)已改變
setChanged();
notifyObservers(page);
}
}
/**
* 文章類
* @author newtrekWang
* @email wangjiaxing20160101@gmail.com
* @time 2018/8/24 0:08
*/
public class Page {
private String date;
private String author;
private String content;
public Page(String date, String author, String content) {
this.date = date;
this.author = author;
this.content = content;
}
@Override
public String toString() {
return "Page{" +
"date='" + date + '\'' +
", author='" + author + '\'' +
", content='" + content + '\'' +
'}';
}
}
// 測試
public static void main(String[] args){
DevTechFrontier devTechFrontier = new DevTechFrontier();
Coder coder1 = new Coder("conder1");
Coder coder2 = new Coder("conder2");
Coder coder3 = new Coder("conder3");
Coder coder4 = new Coder("conder4");
Coder coder5 = new Coder("conder5");
devTechFrontier.addObserver(coder1);
devTechFrontier.addObserver(coder2);
devTechFrontier.addObserver(coder3);
devTechFrontier.addObserver(coder4);
devTechFrontier.addObserver(coder5);
devTechFrontier.postNewPage(new Page(new Date().toString(),"wang","技術(shù)內(nèi)容"));
}
執(zhí)行結(jié)果:
conder5 得到了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術(shù)內(nèi)容'}
conder4 得到了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術(shù)內(nèi)容'}
conder3 得到了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術(shù)內(nèi)容'}
conder2 得到了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術(shù)內(nèi)容'}
conder1 得到了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術(shù)內(nèi)容'}
例子2 BaseAdapter中的notifyDatasetChanged()
我們往ListView添加數(shù)據(jù)后,都會調(diào)用Adapterder的notifyDataChanged()方法刷新顯示數(shù)據(jù)。其實它就是內(nèi)置了一個DataSetObservable和多個DataSetObserver,調(diào)用notifyDataChanged()會讓DataSetObservable一一通知DataSetObserver的onChanged()回調(diào),然后onChanged()會根據(jù)現(xiàn)在的數(shù)據(jù)情況調(diào)用ListView重新布局方法,刷新UI,這就是一個典型的Observer模式。
例子3 BroadcastReceiver 廣播注冊
參考 BroadcastReceiver中的那些設計模式
BroadcastReceiver是Android的四個組件之一,它作為應用內(nèi),進程間的一種重要通信手段,能夠?qū)⒛硞€消息通過廣播的形式傳遞給它注冊的對應廣播接收器的對象。接收對象即BroadcastReceiver,為觀察者,然后它需要通過Context的registerReceiver方法注冊到AMS中,當通過sendBroadcase發(fā)送廣播時,所有注冊了對應的IntentFilter的BroadcastReceiver對象就會接收到這個消息,Broadcast的onReceive方法就會調(diào)用,這就是一個典型的發(fā)布--訂閱模式,只是發(fā)送廣播時會通過IntentFilter作一些匹配過濾操作。
例子4 RxJava
在Android開發(fā)中,我們經(jīng)常需要在兩個不同的業(yè)務場景之間進行通信,比如子線程要發(fā)消息給主線程。我們單靠AndroidSDK里面的API的話,就必須重寫Handler,將主線程中創(chuàng)建的Handler對象傳給子線程,然后子線程通過handler發(fā)送消息,主線程維護的消息隊列收到消息,然后消息又交給handler處理,最終完成消息發(fā)送。
Android創(chuàng)造的AsyncTask和Handler貌似讓異步代碼做得簡潔,但是業(yè)務多了,就不一定了。
反正我覺得自己手寫Handler,懶得寫,經(jīng)常見到的情況是一個Handler來處理多個子線程發(fā)來的消息,不是if-else就是switch case,message還要做一些變換啥的,還要考慮有沒有內(nèi)存泄漏情況,子線程任務要能及時取消等等,有點煩,除非你設計好了一個良好的封裝。
如果你用過RxJava,保證再也不想用Handler了,因為RxJava 運用觀察者模式和鏈式操作符解決了上述很多問題。
RxJava 有四個基本概念:Observable (可觀察者,即被觀察者)、 Observer (觀察者)、 subscribe (訂閱)、事件。Observable 和 Observer 通過 subscribe() 方法實現(xiàn)訂閱關(guān)系,從而 Observable 可以在需要的時候發(fā)出事件來通知 Observer。
使用方法就跟通常的觀察者模式使用差不多,只是通常我們都只是一對一,即一個觀察者對一個被觀察者,具體使用可以看看以下兩位大佬的文章。
給 Android 開發(fā)者的 RxJava 詳解
當初第一次知道觀察者模式就是因為看到這篇文章。
給初學者的RxJava2.0教程 上下游管道例子講得很好。
我不知道一些大廠對這些線程調(diào)度用的是什么方案,不過從網(wǎng)上的大多技術(shù)文章來看,網(wǎng)絡業(yè)務方面Okhttp3+Retrofit2+Rxjava2是很主流的。
例子5 MVVM 中的View-ViewModel
MVVM是Model-View-ViewModel的簡寫。它本質(zhì)上就是MVC 的改進版。MVVM 就是將其中的View 的狀態(tài)和行為抽象化,讓我們將視圖 UI 和業(yè)務邏輯分開。
View-ViewModel簡單來說就是數(shù)據(jù)與視圖組件建立綁定關(guān)系,比如單向綁定
這點微軟的WPF做得很高,WPF天生支持mvvm,只要vm中的數(shù)據(jù)模型值發(fā)生了變化,與之對應的view控件就會自動刷新顯示,不在自己用Controller之類的控制view顯示新數(shù)據(jù)。
其實原理也就是觀察者模式原理,vm為被觀察者,v為觀察者,vm有變化,就會觸發(fā)vm通知v更新。
綁定關(guān)系
- 單向綁定:vm -> v 或者 v -> vm
- 雙向綁定:vm <-> v
應用mvvm
- 使用RxBinding
- 使用Android Jetpack Components 的 DataBinding,LiveData
例子6 各種Bus,EventBus,RxBus
EventBus,RxBus主要是可以解決不同組件之間的通信問題,當然不同線程之間也可以。要比Android的廣播好用點,而且不依賴Contenxt。
單片機有總線,Qt有槽函數(shù),貌似Android就沒有,所以有人就造了Android應用中的總線。
我覺得這些總線的特點就是發(fā)布者是唯一的單例,但可以被多個訂閱者訂閱,跟Rxjava常用的一對一模式有點不同。
EnventBus原理 直接上圖

RxBus也是Rxjava的擴展,只是用的是可以一對多的觀察者
最近不是很流行組件化嗎(分業(yè)務module那種),業(yè)務module之間的通信方案應該肯定優(yōu)選XXBus.