Android 設計模式之觀察者模式

在平常開發過程當中時常須要用到設計模式,可是設計模式有23種,如何將這些設計模式瞭然於胸而且能在實際開發過程當中應用得駕輕就熟呢?和我一塊兒跟着《Android源碼設計模式解析與實戰》一書邊學邊應用吧!

設計模式系列文章

今天咱們要講的是觀察者模式


定義

定義對象間的一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。設計模式

使用場景

  • 關聯行爲場景
  • 事件多級觸發場景
  • 跨系統的信息交換場景,如消息隊列、事件總線的處理機制

使用例子

  • 常見的發佈-訂閱模式
  • ListView的Adapter的notifyDataSetChanged更新方法
  • BroadcastReceiver
  • 開源庫EventBus
  • RxJava

實現

四大角色

  • 抽象主題,也就是被觀察者(Observable),抽象主題把全部的觀察者對象的引用保存在一個集合裏,每一個主題能夠有任意數量的觀察者,抽象主題提供接口,能夠增長和刪除觀察者對象
  • 具體的主題(具體的被觀察者),也就是抽象主題的子類,該角色將有關狀態存入具體觀察者對象,在具體主題內部狀態發生改變時,通知全部註冊過的觀察者
  • 抽象觀察者,觀察者的抽象類,定義了一個更新的接口
  • 具體的觀察者,實現了抽象觀察者的更新接口,在被觀察者狀態發生變化時更新自身的狀態

實現的要點

  • 實現的要點在於把握好以上四個角色
  • 設計模式是一種思想,在應用過程當中能夠根據具體的須要來實現。RxJava和ListView的Adapter都用到了觀察者模式,但實現上確定會有不同

實現方式

利用JDK中Observable類和Observer接口實現

  • JDK中有Observable類和Observer接口
  • 觀察者實現Observer接口,被觀察者繼承Observable類
  • 被觀察者經過Observable類的addObserver方法添加觀察者

觀察者

public class MyObserver implements Observer{
    private String mName;

    public MyObserver(String name) {
        mName = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(mName + "-->" + "update: " + arg);
    }
}
複製代碼
  • Observer接口就一個update方法,Observable表示被觀察者,Object表示被觀察者更新的東西

被觀察者

public class MyObservable extends Observable{
    public void sendChangeMeg(String content) {
        //方法繼承自Observable,標示狀態或是內容發生改變
        setChanged();

        //方法繼承自Observable,通知全部觀察者,最後會調用每一個Observer的update方法
        notifyObservers(content);
    }
}
複製代碼
  • 被觀察者經過setChanged()方法標示改變,經過notifyObservers方法通知全部觀察者
  • notifyObservers方法會遍歷全部的觀察者Observer,並調用它們的update方法
  • notifyObservers方法中的參數就是最後傳到觀察者update方法的參數Object arg

測試

public class ObserverPatternTest {
    @Test
    public void test1() throws Exception {
        MyObservable myObservable = new MyObservable();

        MyObserver myObserver1 = new MyObserver("observer-1");
        MyObserver myObserver2 = new MyObserver("observer-2");
        myObservable.addObserver(myObserver1);
        myObservable.addObserver(myObserver2);

        //發佈消息
        myObservable.sendChangeMeg("發佈更新啦");
    }

}
複製代碼

測試結果

觀察者模式測試結果

  • 不知道你們看到上面的測試結果有沒有什麼疑問,細心的小夥伴可能會發現咱們添加觀察者的時候順序是myObserver一、myObserver2,可是結果輸出確是先myObserver2再myObserver1,爲啥?

看看源碼

Observable的源碼
public class Observable {
    private boolean changed = false;
    private final ArrayList<Observer> observers;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        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);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }
  
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Observer[] arrLocal;

        synchronized (this) {
            if (!hasChanged())
                return;

            arrLocal = observers.toArray(new Observer[observers.size()]);
            clearChanged();
        }

        //注意這裏的起始點
        for (int i = arrLocal.length-1; i>=0; i--)
            arrLocal[i].update(this, arg);
    }

    public synchronized void deleteObservers() {
        observers.clear();
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return observers.size();
    }
}

複製代碼
  • 首先在生成Observable對象時,會初始化一個ArrayList,用於保存全部的觀察者Observer
  • 當咱們調用notifyObservers時,會循環遍歷調用全部添加的觀察者Observer,並調用Observer的update方法,而遍歷的順序是從最後添加的一個Observer開始的,因此會有咱們上面測試結果圖片的狀況
  • 解耦的關鍵就在於,Observer是一個接口,而咱們的觀察者都實現了這個接口
public interface Observer {
    void update(Observable o, Object arg);
}
複製代碼
  • 想一想這樣設計遵循了什麼設計原則呢?若是忘記了咱們前面學得六大原則,能夠去複習一下。
其餘的實現方式,好比ListView的Adapter以及RxJava裏面的觀察者模式,你們也能夠去嘗試看看源碼,找找看觀察者模式中的四大角色

總結

  • 觀察者模式是使用率很高的模式,它的一個重要做用就是解耦,將觀察者個被觀察者解耦
  • 經常使用的地方是GUI系統,發佈-訂閱系統
  • 應用觀察者模式須要考慮開發效率和運行效率

歡迎關注個人微信公衆號,期待與你一塊兒學習,一塊兒交流,一塊兒成長! bash

AntDream
相關文章
相關標籤/搜索