瞭解觀察者模式--通過詳細代碼與例子

觀察者模式

應用場景

建立一個對象與多個對象之間的一對多的依賴關係,一個對象狀態發生改變時將會通知其他對象,發生狀態改變的對象爲subject,變化主體,被通知的對象爲observer,一個subject可以有多個observer,且observer之間相互獨立,可以隨機增減observer。常見的例子有微信的公衆號與關注的人,公衆號爲subject,關注的人爲observer,當有新推送時,所有的observer都能收到消息。

定義

觀察者模式定義了對象之間的一對多依賴關係,被觀察的是有狀態並可以修改狀態的subject(主體),觀察者爲observer,當subject狀態改變時,它的所有observer都會受到通知並自動更新。

例子

我們用微信公衆號與微信用戶做爲例子,一個微信公衆號可以有多個微信用戶關注,當公衆號更新消息時所有關注用戶都能收到信息,一個微信用戶能夠關注多個公衆號,當其中任意公衆號更新時都能收到消息。意思是要實現多對多的依賴關係,但不要忘記,多對多其實包含了一對多,多個一對多的關係就組成了多對多,所以適合使用觀察者模式,公衆號爲subject,用戶爲observer,下面是uml類圖
在這裏插入圖片描述

WechatSubscription是微信公衆號類,實現subject接口,數組observers存儲它的觀察者(微信用戶),有增刪觀察者的方法,同時在發佈文章後可以通知觀察者。
Wechatuser是微信用戶類,實現observer接口,subjects存儲用戶所關注的公衆號,通過update方法獲取公衆號的消息。

下面是代碼
Observer.java

package priv.mxz.observer_pattern;

import sun.rmi.runtime.Log;

import java.util.ArrayList;



interface Observer {
    void update(Subject subject, Object obj);
}



class WechatUser implements Observer{
    private ArrayList<Subject> subjects;
    private String name;

    public WechatUser(String name){
        subjects=new ArrayList<Subject>();
        this.name=name;
    }

    @Override
    public void update(Subject subject, Object obj) {
        if (obj!=null && obj instanceof String) {
            
            System.out.println("wechat user "+ name+" got article from "+ subject);
            System.out.println("article detail: "+obj);
        }
    }

    public void follow(Subject subject){
        if (subject!=null){
            subjects.add(subject);
            subject.registerObserver(this);
        }
    }

    public void unfollow(Subject subject){
        if (subject!=null){
            int index=subjects.indexOf(subject);
            if (index>=0){
                subjects.remove(index);
                subject.removeObserver(this);
            }
        }
    }

}


Subject.java

package priv.mxz.observer_pattern;

import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.scripts.JD;

import java.util.ArrayList;

interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

class WechatSubscription implements Subject{
    private ArrayList<Observer> observers;
    private String article;
    private String name;

    public WechatSubscription(String name){
        observers=new ArrayList<Observer>();
        article="no article now";
        this.name=name;
    }

    @Override
    public void registerObserver(Observer observer) {
        if (observer!=null)
            observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int index=observers.indexOf(observer);
        if (index>=0)
            observers.remove(index);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer:observers) {
            observer.update(this,article);
        }
    }

    public void pushArticle(String article) {
        this.article = article;
        notifyObservers();
    }


}

ObserverPattern.java

package priv.mxz.observer_pattern;

public class ObserverPattern {
    public static void main(String[] args) {

        WechatUser mike=new WechatUser("Mike");
        WechatUser jason=new WechatUser("Jason");
        WechatSubscription taobao=new WechatSubscription("Taobao");
        WechatSubscription alipay=new WechatSubscription("Alipay");
        System.out.println("wechat user mike follow taobao");
        System.out.println("wechat user mike follow alipay");
        mike.follow(taobao);
        mike.follow(alipay);
        System.out.println("wechat user jason follow alipay");
        jason.follow(alipay);

        System.out.println("taobao push first article");
        taobao.pushArticle("taobao first article");
        System.out.println("alipay push first article");
        alipay.pushArticle("alipay first article");
        System.out.println("wechat user jason unfollow alipay");
        mike.unfollow(alipay);
        System.out.println("alipay push second article");
        alipay.pushArticle("alipay second article");


    }
}

先把輸出結果展示出來

wechat user mike follow taobao
wechat user mike follow alipay
wechat user jason follow alipay
taobao push first article
wechat user Mike got article from priv.mxz.observer_pattern.WechatSubscription@1540e19d
article detail: taobao first article
alipay push first article
wechat user Mike got article from priv.mxz.observer_pattern.WechatSubscription@677327b6
article detail: alipay first article
wechat user Jason got article from priv.mxz.observer_pattern.WechatSubscription@677327b6
article detail: alipay first article
wechat user jason unfollow alipay
alipay push second article
wechat user Jason got article from priv.mxz.observer_pattern.WechatSubscription@677327b6
article detail: alipay second article

ObserverPattern中有函數入口,首先定義兩個微信用戶mike和jason,然後定義兩個公衆號taobao和alipay,然後讓mike關注taobao和alipay,jason只關注alipay。隨後taobao發送文章,查看輸出發現只有mike收到了推送,jason沒有,因爲jason沒有關注mike,然後讓alipay也發送文章,此時mike和jason都收到了文章,最後讓mike取消關注alipay,alipay再發送文章只有jason收到了推送。由此可見觀察者模式可以很方便地增刪觀察者,在subject狀態更新時,能及時地把消息發送給所有註冊的觀察者。

java內置的觀察者模式

在JDK的java.util包中,有Observable類和Observer接口,兩者共同實現了java內置的觀察者模式

Observable類

Observable提供瞭如下方法

  1. addObserver()
  2. deleteObserver()
  3. notifyObservers()
  4. serChanged()

繼承Observable類的類充當觀察者模式中subject的角色,子類不需要自己維護所有觀察者的列表,用戶可以決定採用push或者pull的方式更新消息,差別在於調用notifyObservers()還是notifyObservers(Object arg) 前者對應pull方式,後者對應push方法,arg就是subject主動push給觀察者的數據。如果沒有arg,那麼就需要觀察者從subject實例中pull數據

setChanged()方法用於標誌狀態發生變化,在調用notifyObservers()或者notifyObservers(Object arg)前必須調用setChanged(),否則實際上不會通知觀察者。

因爲java不支持多重繼承,所以一個類繼承了Observable類後無法再繼承其他類,有必要的話可以自己實現subject接口來取代Observable類

Observer接口

Observer接口只定義了一個方法
update(Observable o,Object arg)
第一個參數是發送通知的Observable實例,第二個是notifyObservers(Object arg)中的arg,如果arg不爲空,則對應push方式,如果arg爲空,則對應pull方式,觀察者從o中拉取數據。

優缺點

優點

  1. 定義了對象間的一對多依賴關係,且觀察者可以隨時增刪。
  2. 實現了表示層與數據邏輯層的分離,當數據邏輯層發生變化時能及時通知所有相關表示層

缺點

  1. 如果一個觀察目標對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間,而且如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
  2. java自帶的觀察者模式有較大限制,Observable設計成一個類限制了自帶的觀察者模式的使用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章