Android中的設計模式之觀察者模式

參考

  • 《設計模式:可複用面向對象軟件的基礎 》5.7 Observer 觀察者 對象行爲型模式
  • 《設計模式解析》 18.4 Observer模式
  • 《Android源碼設計模式解析與實戰》第12章 解決,解耦的鑰匙--觀察者模式

本人能力有限,若有明顯錯誤,不規範的地方,請指正,謝謝。java

意圖

定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並自動更新。git

別名

依賴(Dependents)發佈-訂閱(Publish-Subscribe)程序員

適用場景

  • 當一個抽象模型有兩個方面,其中一個方面依賴於另外一個方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。
  • 當對一個對象的改變須要同時改變其它對象,而不知道具體有多少對象有待改變。
  • 當一個對象必須通知其它對象,而它又不能肯定其它對象是誰。

結構

觀察者模式結構

  • subject 抽象主題,也就是被觀察的角色,抽象主題角色把全部觀察者對象的引用保存在一個集合裏,每一個主題均可以由任意數量的觀察者,抽象主題提供一個接口,能夠增長和刪除觀察者對象。
  • ConcreteSubject 具體主題,也就是具體被觀察者角色
  • Observer 抽象觀察者,他定義了一個更新接口,使得訂閱的主題更改時更新到本身。
  • ConcreteObserver 具體觀察者

優勢

  • 觀察者與被觀察者之間是抽象耦合,應對業務變化。
  • 加強系統靈活性,可擴展性。

缺點

在應用觀察者模式時須要考慮一些開發效率和運行效率問題,程序中包括一個被觀察者,多個觀察則,開發和調式等內容會比較複雜。github

例子

Observer模式是直接接觸過的最多見的設計模式之一,GUI程序應用得比較廣。設計模式

例子1 程序員訂閱Android博客週刊

描述

開發技術前線網站是一個聚集各類技術文章的網站,它支持郵箱訂閱,一旦有用戶訂閱了它,每當網站出新內容時,會自動將新內容推送到用戶郵箱。網絡

由於java核心庫裏已經有了Observer抽象觀察者接口和Observable抽象被觀察者類,因此咱們直接實現和繼承它們便可擴展咱們本身的業務。異步

簡單代碼實現

/**
 * 程序員是訂閱者,就是具體的觀察者
 *
 * @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 + '\'' +
                '}';
    }
}

/**
 *  開發技術網站
 * @author newtrekWang
 * @email  wangjiaxing20160101@gmail.com
 * @time   2018/8/24  0:16
 */
public class DevTechFrontier extends Observable {
    /**
     * 通知全部觀察者
     * @param page 新的文章
     */
    public void postNewPage(Page page){
        // 設置狀態已改變
        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","技術內容"));
    }

執行結果:mvvm

conder5  獲得了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術內容'}
conder4  獲得了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術內容'}
conder3  獲得了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術內容'}
conder2  獲得了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術內容'}
conder1  獲得了文章:Page{date='Fri Aug 24 00:19:55 CST 2018', author='wang', content='技術內容'}

例子2 BaseAdapter中的notifyDatasetChanged()

咱們往ListView添加數據後,都會調用Adapterder的notifyDataChanged()方法刷新顯示數據。其實它就是內置了一個DataSetObservable和多個DataSetObserver,調用notifyDataChanged()會讓DataSetObservable一一通知DataSetObserver的onChanged()回調,而後onChanged()會根據如今的數據狀況調用ListView從新佈局方法,刷新UI,這就是一個典型的Observer模式。ide

例子3 BroadcastReceiver 廣播註冊

參考 BroadcastReceiver中的那些設計模式

BroadcastReceiver是Android的四個組件之一,它做爲應用內,進程間的一種重要通訊手段,可以將某個消息經過廣播的形式傳遞給它註冊的對應廣播接收器的對象。接收對象即BroadcastReceiver,爲觀察者,而後它須要經過Context的registerReceiver方法註冊到AMS中,當經過sendBroadcase發送廣播時,全部註冊了對應的IntentFilter的BroadcastReceiver對象就會接收到這個消息,Broadcast的onReceive方法就會調用,這就是一個典型的發佈--訂閱模式,只是發送廣播時會經過IntentFilter做一些匹配過濾操做。函數

例子4 RxJava

在Android開發中,咱們常常須要在兩個不一樣的業務場景之間進行通訊,好比子線程要發消息給主線程。咱們單靠AndroidSDK裏面的API的話,就必須重寫Handler,將主線程中建立的Handler對象傳給子線程,而後子線程經過handler發送消息,主線程維護的消息隊列收到消息,而後消息又交給handler處理,最終完成消息發送。

Android創造的AsyncTask和Handler貌似讓異步代碼作得簡潔,可是業務多了,就不必定了。
反正我以爲本身手寫Handler,懶得寫,常常見到的狀況是一個Handler來處理多個子線程發來的消息,不是if-else就是switch case,message還要作一些變換啥的,還要考慮有沒有內存泄漏狀況,子線程任務要能及時取消等等,有點煩,除非你設計好了一個良好的封裝。

若是你用過RxJava,保證不再想用Handler了,由於RxJava 運用觀察者模式和鏈式操做符解決了上述不少問題。

RxJava 有四個基本概念:Observable (可觀察者,即被觀察者)、 Observer (觀察者)、 subscribe (訂閱)、事件。Observable 和 Observer 經過 subscribe() 方法實現訂閱關係,從而 Observable 能夠在須要的時候發出事件來通知 Observer。

使用方法就跟一般的觀察者模式使用差很少,只是一般咱們都只是一對一,即一個觀察者對一個被觀察者,具體使用能夠看看如下兩位大佬的文章。

給 Android 開發者的 RxJava 詳解
當初第一次知道觀察者模式就是由於看到這篇文章。

給初學者的RxJava2.0教程 上下游管道例子講得很好。

我不知道一些大廠對這些線程調度用的是什麼方案,不過從網上的大多技術文章來看,網絡業務方面Okhttp3+Retrofit2+Rxjava2是很主流的。

例子5 MVVM 中的View-ViewModel

MVVM是Model-View-ViewModel的簡寫。它本質上就是MVC 的改進版。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。

View-ViewModel簡單來講就是數據與視圖組件創建綁定關係,好比單向綁定
這點微軟的WPF作得很高,WPF天生支持mvvm,只要vm中的數據模型值發生了變化,與之對應的view控件就會自動刷新顯示,不在本身用Controller之類的控制view顯示新數據。

其實原理也就是觀察者模式原理,vm爲被觀察者,v爲觀察者,vm有變化,就會觸發vm通知v更新。

綁定關係

  • 單向綁定:vm -> v 或者 v -> vm
  • 雙向綁定:vm <-> v

應用mvvm

  1. 使用RxBinding
  2. 使用Android Jetpack Components 的 DataBinding,LiveData

例子6 各類Bus,EventBus,RxBus

EventBus,RxBus主要是能夠解決不一樣組件之間的通訊問題,固然不一樣線程之間也能夠。要比Android的廣播好用點,並且不依賴Contenxt。

單片機有總線,Qt有槽函數,貌似Android就沒有,因此有人就造了Android應用中的總線。

我以爲這些總線的特色就是發佈者是惟一的單例,但能夠被多個訂閱者訂閱,跟Rxjava經常使用的一對一模式有點不一樣。

EnventBus原理 直接上圖

EnventBus 結構

RxBus也是Rxjava的擴展,只是用的是能夠一對多的觀察者

最近不是很流行組件化嗎(分業務module那種),業務module之間的通訊方案應該確定優選XXBus.

相關文章
相關標籤/搜索