觀察者模式 —— java.util.Observable + java.util.Observer 源碼學習


學習觀察者模式,結合JavaJDK的內置觀察者模式代碼一起學習package java.util;


//不是抽象類,也不是接口
public class Observable { private boolean changed = false; //狀態標志 private Vector<Observer> obs; //儲存觀察者的集合引用 public Observable() { obs = new Vector<>(); //構造函數中構建空的集合 } /*增加觀察者*/ public synchronized void addObserver(Observer o) { if (o == null) // 判空 throw new NullPointerException(); if (!obs.contains(o)) { //不重復 obs.addElement(o); } } /*刪除一個被觀察者*/ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /**/ public void notifyObservers() { notifyObservers(null); } /*向所有注冊的觀察者發送更新通知,參數是更新的信息
  
*/ public void notifyObservers(Object arg) { /* * .一個臨時的數組緩存,用於當前被觀察者(集合)的狀態的一個快照 */ Object[] arrLocal; synchronized (this) {
      /*從集合中提取每個觀察者的代碼需要同步,但是通知觀察者的代碼不需要(不應該)。
        潛在的最壞的結果是:
        (1)新增的觀察者錯過了正在進行的通知
        (2)未注冊的觀察者被通知到了
     */
            if (!changed) //判斷標志 return; arrLocal = obs.toArray(); //集合轉化成數組 clearChanged(); //標志恢復成false 不需要通知更新 } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); //每個觀察者的update方法需要的參數是兩個,主題和參數 } /** * 清除全部的觀察者 */
    public synchronized void deleteObservers() { obs.removeAllElements(); } /*將標志改為true ,主題(被觀察者)發生改變了*/
    protected synchronized void setChanged() { changed = true; } /*標志更新為false, 主題沒有變動,或者是變動了但是已經通知過了*/
    protected synchronized void clearChanged() { changed = false; } /*查看是否發生改變(返回就是標志)*/
    public synchronized boolean hasChanged() { return changed; } /*返回觀察者的數量*/
    public synchronized int countObservers() { return obs.size(); } }

 

這個類的主要構成要點:

1、包含所有觀察者的(空)的集合 + 對這個集合的管理操作(增減,查看數量)

2、包含一個(是否變動)標志 + 對這個標志的管理操作(設置、查看)

3、向所有觀察者發送通知

 

 

繼續學習觀察者模式,JavaJDK中java.util.Observer源碼學習:

package java.util;

/*這是觀察者的接口,關鍵點就是它有一個update方法,每當被通知修改時候,就是這個方法被調用
 */
public interface Observer {
    void update(Observable o, Object arg);
}

 

使用這兩個內置的觀察者模式類,參考方法:

(1)新建一個具體主題類繼承Observable,  不需要重寫它的任何方法,只要添加自己需要的業務邏輯: 設置傳遞的參數信息,發送消息的方法……

  唯一需要注意的是:兩種通知方式,參數的獲取方法不同

public class ConcreteSubjectA extends Observable {
    //保存數據
    private String name;

    //設置參數信息
    public void setInfo(String a){
        this.name=a;
    }

    //觸發更新
    public void sendNotify(){
        setChanged(); //必須先要設置一下標志,告知已經改了
        //通知方式一;讓觀察者自己按需拉取(所以傳參是空)
        notifyObservers();
        //通知方式二:直接把信息全部推送
        notifyObservers(name);
    }
    
    //如果是拉取的通知方式,還要給出參數的獲取方法
    public String getName() {
        return name;
    }
}

(2) 新建觀察者類,實現Observer接口,這里通常是有主題的引用的,這樣可以自己完成注冊,注銷,和取數據

public class FirstObserver implements Observer {
    private Observable observable;

    public FirstObserver(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);// 在構造的時候就可以把自己注冊了,不用手動添加了,如果是改造已經存在的實體類,還是需要手動添加
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof ConcreteSubjectA){
            String name = ((ConcreteSubjectA) o).getName(); //拉取的通知方式,arg是null. 要自己獲取
            //具體更新操作…… 略……
        }
    }
}

測試類:

public class Test {
    public static void main(String[] args) {
        ConcreteSubjectA ca=new ConcreteSubjectA();
        FirstObserver firstObserver = new FirstObserver(ca); //構造時候注冊
        ca.setInfo("我是新名字1");
        ca.sendNotify(); //自己寫的通知方法,不是父類的notify
        ca.setInfo("我是新名字2");
        ca.sendNotify();
        ca.setInfo("我是新名字3");
        ca.sendNotify();
    }
}
/*
運行后輸出:
已經更新啦……我是新名字1
已經更新啦……我是新名字2
已經更新啦……我是新名字3
 */

 

java.util.Observable存在的問題:

1、它是一個類,而不是一個接口,限制了它的復用。 想要用它就必須繼承它,那就不能再繼承其它類。

2、setChanged() 等方法是被保護的,除了繼承,無法使用它。違反“多用組合,少用繼承”的設計原則。



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM