Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> java/android 設計模式學習筆記(2)---觀察者模式

java/android 設計模式學習筆記(2)---觀察者模式

編輯:關於Android編程

這篇來講一下觀察者模式,觀察者模式在實際項目中使用的也是非常頻繁的,它最常用的地方是GUI系統、訂閱——發布系統等。因為這個模式的一個重要作用就是解耦,使得它們之間的依賴性更小,甚至做到毫無依賴。以GUI系統來說,應用的UI具有易變性,尤其是前期隨著業務的改變或者產品的需求修改,應用界面也經常性變化,但是業務邏輯基本變化不大,此時,GUI系統需要一套機制來應對這種情況,使得UI層與具體的業務邏輯解耦,觀察者模式此時就派上用場了。
PS:對技術感興趣的同鞋加群544645972一起交流。

特點

觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象狀態發生改變時,它的所有依賴者都會收到通知並自動更新。它實現了 Subject 和 Observer 之間的松耦合,Subject只知道觀察者實現了 Observer 接口,主題不需要知道具體的類是誰、做了些什麼或其他任何細節。任何時候我們都可以增加新的觀察者。因為主題唯一依賴的東西是一個實現Observer 接口的對象列表,所以我們可以隨時增加觀察者,同樣的,也可以在任何時候刪除觀察者,當然更不用去關心觀察者的類型,只要實現了Observer接口即可,Subject 最後只會發通知給實現了 Observer 接口的觀察者。Subject 和 Observer 之間實現松耦合之後,雙方代碼的修改都不會影響到另外一方,當然前提是雙方得遵守接口的規范(接口隔離原則)。
觀察者模式使用的場景:

關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是“組合”關系;事件多級觸發場景;跨系統的消息交換場景,如消息隊列、事件總線的處理機制。EventBus 框架就是一個使用觀察者模式的典型例子。

 

UML類圖

  這裡寫圖片描述vcfJq7m5s8k8L3A+DQpTdWJqZWN0o7qz6c/z1vfM4qOs0rK+zcrHsbu527LstcS9x8mro6yz6c/z1vfM4rDRy/nT0LnbsuzV37bUz/O1xNL908Oxo7Tm1NrSu7j2vK+6z8Dvo6zDv7j21vfM4ra8v8nS1NPQyM7S4sr9wb+1xLnbsuzV36Oss+nP89b3zOLM4bmp0ru49r3Tv9qjrL/J0tTU9rzTus3JvrP9uduy7NXfttTP86GjQ29uY3JldGVTdWJqZWN0o7q+38zl1vfM4qOsuMO9x8mrvavT0LnY17TMrLTmyOu+38zluduy7NXfttTP86Os1Nq+38zl1vfM4rXExNqyv9e0zKy3osn6uMSx5Mqxo6y4+Mv509DXorLhuf21xLnbsuzV37eis/bNqNaqo6y+38zl1vfM4r3Hyau21M/z09a90Nf2vt/M5bG7uduy7NXfvcfJq6GjT2JzZXJ2ZXKjurPpz/O527Ls1d+jrLjDvcfJq8rHuduy7NXftcSz6c/zwOCjrMv8tqjS5cHL0ru49rj80MK907/ao6zKubXD1Nq1w7W91vfM4rXEuPy4xM2o1qrKsbj80MLX1Ly6oaNDb25jcmV0ZU9ic2VydmVyo7q+38zltcS527Ls1d+jrLjDttTP88q1z9az6c/zuduy7NXfvcfJq8v5tqjS5bXEuPzQwr3Tv9qjrNLUsePU2tb3zOK1xNe0zKyx5LuvyrG4/NDC19TJ7bXE17TMrKGjoaGhodPQvLi149Do0qrXotLitcTKx1N1YmplY3Qgus0gT2JzZXJ2ZXIgysfSu7j20ru21LbgtcS52M+1o6zSsr7NysfLtbnbsuzV39a70qrKtc/WIE9ic2VydmVyIL3Tv9qyorDR19S8uteisuG1vSBTdWJqZWN0INbQvs3E3Lm7vdPK1bW9z/vPosrCvP6ju0phdmEgQVBJ09DE2tbDtcS527Ls1d/Eo8q9wOCjumphdmEudXRpbC5PYnNlcnZhYmxlIMDgus0gamF2YS51dGlsLk9ic2VydmVyIL3Tv9qjrNXit9ax8LbU06bXxSBTdWJqZWN0ILrNIE9ic2VydmVyILXEvcfJq6O7yrnTwyBKYXZhIEFQSSC1xLnbsuzV38Sjyr3A4KOs0OjSqtei0uK1xMrHsbu527Ls1d/U2rX308Mgbm90aWZ5T2JzZXJ2ZXJzKCkguq/K/c2o1qq527Ls1d/Wrsew0ru2qNKqtffTwyBzZXRDaGFuZ2VkKCkguq/K/aOs0qqyu8i7uduy7NXfzt63qL3Ttb3NqNaqo7vKudPDIEphdmEgQVBJILXEyLG149KyutzD98/Uo6zTydPaIE9ic2VydmFibGUgysfSu7j2wOCjrGphdmEg1rvUytDttaW8zLPQtcTIsbXjvs21vNbCxOPI57n7zazKsc/r0qq78cihwe3Su7j2uLjA4LXEyvTQ1Mqxo6zE49a7xNzRodTxysrF5Mb3xKPKvbvy1d/Kx8Tasr/A4LXEt73KvaOstvjH0tPJ09ogc2V0Q2hhbmdlZCgpILqvyv3OqiBwcm90ZWN0ZWQgyvTQ1KOsy/nS1MTjs/23x7zMs9AgT2JzZXJ2YWJsZSDA4KOst/HU8sTjuPmxvs7et6jKudPDuMPA4LXEyvTQ1KOs1eLSss6lsbPBy8novMbEo8q9tcTUrdTyo7q24NPD1+m6z6OsydnTw7zMs9Chow0KPHA+Jm5ic3A7PC9wPg0KPGgyIGlkPQ=="示例與源碼">示例與源碼

觀察者模式寫法有很多種,只要遵循將被觀察者和觀察者解耦思想的方法都是可以的,列舉三種我在開發中常見的方法,以 firstmoduel 作為 secondmodule1 和 secondmodule2 的父 module ,最後 application 以 secondmoduel1 和secondmodule2 作為父 module,形成一個菱形的關系為例:

Java API

使用Java API的寫法其實就是使用 java.util.Observable 類和 java.util.Observer 接口,根據 uml 類圖構造觀察者,我們簡單實現一個在 secondmodule2 中通知 secondmodule1有數據源的改變,並且讓 secondmodule1 打印出對應日志的功能。因為 secondmodule1 和 secondmodule2 是一個同級關系,所以無法相互調用,只能通過 firstmodule 作為主題使用觀察者模式進行通信。
Subject 在使用 Java API 時其實就是 Observable 類,所以我們要在 firstmodule 中繼承該類實現一個 ConcreteSubject 用來保存所有的觀察者,接著在 secondmodule1 中實現 Observer 接口來生成一個觀察者,最後在secondmodule2 中通知 firstmodule 數據源改變,最後通過 firstmodule 通知到 secondmodule1 ,代碼如下:
firstmoduel/DataObservable.class 中利用單例模式進行觀察者的管理和通知:

public class DataObservable extends Observable{

    private static volatile DataObservable instance;
    private DataObservable() {}

    public static DataObservable getInstance() {
        if (instance == null){
            synchronized (DataObservable.class) {
                if (instance == null){
                    instance = new DataObservable();
                }
            }
        }
        return instance;
    }

    public void notifyDataChanged(DataBean data) {
        setChanged();
        notifyObservers(data);
    }
}

firstmoduel/DataBean.class 數據類:

public class DataBean {
    public int temperature;
}

secondmodule1/DataObserver.class 中判斷如果為正確的 Observerable 則打印日志:

public class DataObserver implements Observer {
    private static final String TAG = "DataObserver";

    @Override
    public void update(Observable observable, Object data) {
        if (observable instanceof DataObservable){
            if (data instanceof DataBean){
                Log.e(TAG, ((DataBean)data).temperature+"");
            }
        }
    }
}

secondmodule2/DataNotify.class

public class DataNotify {
    public static void notifyDataChanged(){
        DataBean bean = new DataBean();
        bean.temperature = (int) (Math.random() * 40);
        DataObservable.getInstance().notifyDataChanged(bean);
    }
}

最後在 MainActivity 中調用 DataNotify 類的相應方法通知 DataObserver 溫度數據變更即可:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    DataObservable.getInstance().addObserver(new DataObserver());
}

@Override
protected void onDestroy() {
    super.onDestroy();
    DataObservable.getInstance().deleteObserver(new DataObserver());
}
...
public void onClick(View v) {
    if (v.getId() == R.id.btn_data_change_1){
        DataNotify.notifyDataChanged();
    }
    ...
}

最後能夠成功打印出正確日志,也就是實現了 android 中兩個同級 module 之間的通信。
當然如果覺得麻煩不使用 Java API 的這兩個類,自己去實現的話也是完全可以的,而且有時可操縱性會比 Java API 更好一些。

multi Observer

有時候我們不一定全部要實現自同一個 Observer 接口,根據實際情況我們可以在完全解耦的情況下將多個 Observer 注冊到 Subject 中,並且根據情況只通知到我們想要通知的一個 Observer 中,聽起來很亂,其實看一張 uml 圖就清楚了:  圖片
這種方式其實最重要的就是 Subject 這個角色了,他承擔了大部分的工作量,我們先實現若干個 Observer 的角色:
firstmodule/IDataListenerOne.class

public interface IDataListenerOne {
    void OnDataChanged(DataBean data);
}

firstmodule/IDataListenerTwo.class

public interface IDataListenerTwo {
    void OnDataChanged(DataBean data);
}

非常簡單的幾個接口,用來在 secondmodule1 和 secondmodule2 之間進行通信。接著是最重要的 Subject 和 ConcreteSubject 角色:
firstmodule/IMultiDataObservable.class

public interface IMultiDataObservable {
    /**
     * 增加觀察者
     */
    void addObserver(Object observer);

    /**
     * 刪除觀察者
     */
    void deleteObserver(Object observer) throws IllegalArgumentException;

    /**
     * 查找觀察者
     */
    ArrayList findObserver(Class clazz);
}

firstmodule/MultiDataObservable.class

public class MultiDataObservable implements IMultiDataObservable {

    private static volatile MultiDataObservable instance;
    private ArrayList

這個類主要是有三個方法,增刪找,特別是 findObserver 方法,它通過傳入的 Class 對象從觀察者 list 中找到符合要求的 Observer 並且返回,需要特別留意的一點是需要使用的方法是 Class.isInstance(Object object) 方法,而不是直接判斷 object.getclass() == clazz ,因為後者的這種判斷如果使用的是匿名內部類方式,他的類名會是 DataCommunicate$1 這種樣式,所以這種方法是不可行的。最後在 secondmodule1 和 secondmodule2 中只要根據使用調用相應的方法即可,以secondmodule2 為例:
secondmodule2/DataCommunicate.class

public class DataCommunicate {
    private static final String TAG = "DataCommunicate";

    private static IDataListenerTwo listenerTwo = null;

    public static void registerDataListenerTwo() {
        IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
        listenerTwo = new IDataListenerTwo() {
            @Override
            public void OnDataChanged(DataBean data) {
                Log.e(TAG, data.temperature + "");
            }
        };
        dataObservable.addObserver(listenerTwo);
    }

    public static void notifyDataListenerOne() {
        IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
        ArrayList lists = dataObservable.findObserver(IDataListenerOne.class);
        DataBean bean = new DataBean();
        bean.temperature = (int) (Math.random() * 40);
        for (IDataListenerOne listener : lists) {
            listener.OnDataChanged(bean);
        }
    }

    public static void unRegisterDataListenerTwo() {
        IMultiDataObservable dataObservable = MultiDataObservable.getInstance();
        dataObservable.deleteObserver(listenerTwo);
    }
}

最後也可以成功通信,這種方式的優點是完全可以自定義 Observer 這個接口,接口中的方法可以任意定義,具有很大的自由性,便於統一管理所有的觀察者,非常方便。

EventBus

事件總線大家想必已經看見過很多了,它也是一個典型的觀察者模式用例,列舉一下我常見的3個框架:

greenrobot/EventBusAndroidEventBus">AndroidEnventBusOtto它們原理也不復雜, 對比之類的網上資料很多,比如Otto 框架的效率大多數時候會比不上 EventBus 等,感興趣想要具體了解的自己動動手收集一下,我這裡以使用最廣泛的 greenrobot/EventBus 為例,來實現 secondmodule1 和 secondmodule2 之間的通信:
secondmodule1/EventNotifier.class

 

public class EventNotifier {
    private static volatile EventNotifier instance;

    private EventNotifier() {
    }

    public static EventNotifier getInstance() {
        if (instance == null) {
            synchronized (EventNotifier.class) {
                if (instance == null) {
                    instance = new EventNotifier();
                }
            }
        }
        return instance;
    }

    public void sendEvent() {
        DataBean bean = new DataBean();
        bean.temperature = (int) (Math.random() * 40);
        EventBus.getDefault().post(bean);
    }
}

secondmodule2/EventObserver.class

public class EventObserver {
    private static final String TAG = "EventObserver";
    private static volatile EventObserver instance;

    private EventObserver() {

    }

    public static EventObserver getInstance() {
        if (instance == null) {
            synchronized (EventObserver.class) {
                if (instance == null) {
                    instance = new EventObserver();
                }
            }
        }
        return instance;
    }

    public void registerObserver() {
        EventBus.getDefault().register(this);
    }

    public void unRegisterObserver() {
        EventBus.getDefault().unregister(this);
    }

    @Subscribe
    public void onEventMainThread(DataBean bean) {
        Log.e(TAG, bean.temperature + "");
    }
}

特別需要注意的是,onEventMainThread 函數需要加上 @Subscribe 注解,要不然是無法工作。最後在 MainActivity 中調用相關函數即可:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    EventObserver.getInstance().registerObserver();
}

@Override
protected void onDestroy() {
    ...
    EventObserver.getInstance().unRegisterObserver();
}

@Override
public void onClick(View v) {
    ...
    else if (v.getId() == R.id.btn_data_change_3) {
        EventNotifier.getInstance().sendEvent();
    }
}

最後當然也是能夠成功!

總結

觀察者模式在實際開發過程中使用的場景真的挺多,比如 Activity 中兩個 fragment 之間的通信,上面描述的兩個同級 module 之間的通信,上層 module 與下層 module 的通信,兩個 Activity 之間維護一個數據源需要做到數據實時同步等等場景,使用觀察者模式之後思路簡單清晰,可維護性好,而且最重要當然是耦合性低利於擴展,Android 官方源碼中的 Listview 和 Adapter 也是使用了觀察者模式的典型例子。
觀察者模式的優點和缺點總結一下,優點:

觀察者和被觀察者之間是抽象耦合,應對業務變化能力強,可擴展性強;觀察者模式支持廣播通訊。被觀察者會向所有的登記過的觀察者發出通知。同時觀察者模式也有一些缺點:觀察者模式有時會有開發效率和運行效率的問題,程序中包括一個被觀察者和多個觀察者時,調試和開發會相對變得復雜;觀察者如果數量很多,所有的觀察者都通知到會花費一定時間;觀察者模式中被觀察者通知觀察者時默認是順序執行,所以當一個觀察者執行緩慢或者卡頓就會影響整體的執行效率,如果有這種情況最好采用異步執行的方式去處理;如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰,在使用觀察者模式時要特別注意這一點。

源碼下載

https://github.com/zhaozepeng/Design-Patterns/tree/master/ObserverPattern

 

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved