學習觀察者模式,結合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() 等方法是被保護的,除了繼承,無法使用它。違反“多用組合,少用繼承”的設計原則。