Java設(shè)計模式 - 觀察者模式

定義

  • 觀察者 Observer,也稱訂閱者 Subscriber
  • 被觀察者 Observable,也稱主題 Subject

定義對象間(被觀察者和觀察者)一種一對多的依賴關(guān)系,使得每當(dāng)一個對象(被觀察者)改變狀態(tài)時,則所有依賴于它的對象(觀察者)都能得到通知并被自動更新。類似于發(fā)布/訂閱模式,Observer訂閱主題,Observable則負(fù)責(zé)發(fā)布內(nèi)容,從而將二者聯(lián)系起來。

使用場景

  • 用于解決解耦問題,觀察者和被觀察者高度分離,只依賴于ObserverObservable抽象;
  • 關(guān)聯(lián)行為場景,需要注意的是,關(guān)聯(lián)行為是可拆分的,而不是“組合”關(guān)系;
  • 事件多級觸發(fā)場景;
  • 跨系統(tǒng)消息交換場景,如消息隊列,事件總線的處理機制。

Andorid源碼實例

  • ListView適配器AdapternotifyDataSetChanged()方法,在方法中遍歷所有的觀察者,回調(diào)其onChanger()方法;
  • BroadcastReceiver的注冊與觸發(fā),通過Context.registerReceiver()方法將BroadcastReceiver(相當(dāng)于觀察者)注冊到AMS中,當(dāng)通過sendBroadcast()發(fā)送廣播時(相當(dāng)于主題數(shù)據(jù)發(fā)生變更),所有注冊了對應(yīng)IntentFilter actionBroadcastReceiver將會被執(zhí)行;
  • EventBus消息總線
  • java.util包中也有ObservableObserver的默認(rèn)實現(xiàn),可參考使用

UML類圖

image-20200502000759111.png

代碼示例

首先定義抽象觀察者,觀察者只關(guān)心主題的變更

public interface Observer {

    /**
     * 處理數(shù)據(jù)更新的方法(在主題發(fā)生變更時由主題主動觸發(fā))
     *
     * @param message 變更的數(shù)據(jù)部分
     */
    void update(String message);

}

接著定義抽象主題(作為被觀察者,也有命名為Observable),主題是供觀察者觀察的對象

public interface Subject {

    /**
     * 訂閱操作
     * @param observer 觀察者
     */
    void registerObserver(Observer observer);

    /**
     * 取消訂閱操作
     * @param observer 觀察者
     */
    void removeObserver(Observer observer);

    /**
     * 通知變動
     */
    void notifyChanged();

}

接著實現(xiàn)觀察者,對主題變更作具體處理

public class Subscriber implements Observer {

    private String name;
    private String message;

    public Subscriber(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        this.message = message;
        read();
    }

    private void read() {
        System.out.println(name + " 收到新消息: " + message);
    }
}

最后再實現(xiàn)主題

public class Publisher implements Subject {

    private String message;

    private List<Observer> observerList = new ArrayList<>();

    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observerList.remove(observer);
    }

    @Override
    public void notifyChanged() {
        for (Observer observer : observerList) {
            observer.update(message);
        }
    }

    public void pushMessage(String message) {
        this.message = message;
        System.out.println("出版社發(fā)布新消息: " + message);
        // 推送消息
        notifyChanged();
    }
}

作如下測試:

public class Test {

    public static void main(String[] args) {

        Subscriber john = new Subscriber("John");
        Subscriber jack = new Subscriber("jack");
        Subscriber tom = new Subscriber("tom");

        Publisher publisher = new Publisher();
        publisher.registerObserver(john);
        publisher.registerObserver(jack);
        publisher.registerObserver(tom);

        publisher.pushMessage("Today is Monday");
        publisher.removeObserver(jack);
        publisher.pushMessage("Today is Tuesday");
    }
}

輸出結(jié)果如下:

出版社發(fā)布新消息: Today is Monday
John 收到新消息: Today is Monday
jack 收到新消息: Today is Monday
tom 收到新消息: Today is Monday
出版社發(fā)布新消息: Today is Tuesday
John 收到新消息: Today is Tuesday
tom 收到新消息: Today is Tuesday

優(yōu)點

  • 觀察者和被觀察者之間是抽象耦合,可靈活應(yīng)對業(yè)務(wù)變化;
  • 增強系統(tǒng)靈活性,可擴展性。

缺點

  • 運用時需要考慮開發(fā)效率和運行效率問題,程序中包括一個被觀察者、多個觀察者、開發(fā)和調(diào)試等內(nèi)容會比較復(fù)雜;
  • 在Java中消息的通知默認(rèn)是順序執(zhí)行,一個觀察者卡頓,會影響其他觀察者的執(zhí)行,所以通常情況下會采用異步的方式實現(xiàn)update()方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容