Java觀察者設(shè)計(jì)模式(Observable和Observer)
觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽某一個(gè)主題對(duì)象。
這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,讓它們能夠自動(dòng)更新自己。
一、觀察者模式介紹
在Java中通過Observable類和Observer接口實(shí)現(xiàn)了觀察者模式。一個(gè)Observer對(duì)象監(jiān)視著一個(gè)Observable對(duì)象的變化,當(dāng)Observable對(duì)象發(fā)生變化時(shí),Observer得到通知,就可以進(jìn)行相應(yīng)的工作。
如果畫面A是顯示數(shù)據(jù)庫(kù)里面的數(shù)據(jù),而畫面B修改了數(shù)據(jù)庫(kù)里面的數(shù)據(jù),那么這時(shí)候畫面A就要重新Load。這時(shí)候就可以用到觀察者模式
二、觀察者模式實(shí)現(xiàn)方法
java.util.Observable中有兩個(gè)方法對(duì)Observer特別重要
①setChanged()方法
/** * Sets the changed flag for this {@code Observable}. After calling * {@code setChanged()}, {@code hasChanged()} will return {@code true}. */ protected void setChanged() { changed = true; } ②notifyObservers()方法 / notifyObservers(Object data)方法 [java] view plaincopy /** * If {@code hasChanged()} returns {@code true}, calls the {@code update()} * method for every observer in the list of observers using null as the * argument. Afterwards, calls {@code clearChanged()}. * <p> * Equivalent to calling {@code notifyObservers(null)}. */ public void notifyObservers() { notifyObservers(null); } /** * If {@code hasChanged()} returns {@code true}, calls the {@code update()} * method for every Observer in the list of observers using the specified * argument. Afterwards calls {@code clearChanged()}. * * @param data * the argument passed to {@code update()}. */ @SuppressWarnings("unchecked") public void notifyObservers(Object data) { int size = 0; Observer[] arrays = null; synchronized (this) { if (hasChanged()) { clearChanged(); size = observers.size(); arrays = new Observer[size]; observers.toArray(arrays); } } if (arrays != null) { for (Observer observer : arrays) { observer.update(this, data); } } }
以上兩個(gè)方法十分重要
setChanged()方法 ——
用來(lái)設(shè)置一個(gè)內(nèi)部標(biāo)志位注明數(shù)據(jù)發(fā)生了變化
notifyObservers()方法 / notifyObservers(Object data)方法 ——
通知所有的Observer數(shù)據(jù)發(fā)生了變化,這時(shí)所有的Observer會(huì)自動(dòng)調(diào)用復(fù)寫好的update(Observable observable, Object data)方法來(lái)做一些處理(比如說畫面數(shù)據(jù)的更新)。
我們可以看到通知Observer有兩個(gè)方法,一個(gè)無(wú)參,一個(gè)有參。那么這個(gè)參數(shù)有什么作用呢?
其中一個(gè)作用:現(xiàn)在我不想通知所有的Observer,而只想其中一個(gè)指定的Observer做一些處理,那么就可以傳一個(gè)參數(shù)作為ID,然后在所有的Observer中判斷,每個(gè)Observer判斷只有接收到底參數(shù)ID是屬于自己的才做一些處理。
當(dāng)然參數(shù)還有其他作用,我只是舉了個(gè)例子。
下面舉個(gè)例子加以說明:
import java.util.Observable; /** * 被觀察者類 */ public class SimpleObservable extends Observable { private int data = 0; public int getData(){ return data; } public void setData(int i){ if(this.data != i) { this.data = i; setChanged(); //只有在setChange()被調(diào)用后,notifyObservers()才會(huì)去調(diào)用update(),否則什么都不干。 notifyObservers(); } } }
上面這個(gè)類是一個(gè)被觀察者類,它繼承了Observable類,表示這個(gè)類是可以被觀察的。
然后在setData()方法里面,也就是數(shù)據(jù)改變的地方,來(lái)調(diào)用Observable類的setChanged()方法和notifyObservers()方法,表示數(shù)據(jù)已改變并通知所有的Observer調(diào)用它們的update()方法做一些處理。
注意:只有在setChange()被調(diào)用后,notifyObservers()才會(huì)去調(diào)用update(),否則什么都不干。
/** * 觀察者類 */ public class SimpleObserver implements Observer { public SimpleObserver(SimpleObservable simpleObservable){ simpleObservable.addObserver(this ); } public void update(Observable observable ,Object data){ // data為任意對(duì)象,用于傳遞參數(shù) System.out.println(“Data has changed to” + (SimpleObservable)observable.getData()); } }
通過生成被觀察者(SimpleObservable類)的實(shí)例,來(lái)調(diào)用addObserver(this)方法讓觀察者(SimpleObserver類)達(dá)到觀察被觀察者(SimpleObservable類)的目的。
然后還要復(fù)寫update()方法,做數(shù)據(jù)改變后的一些處理。
下面可以寫一個(gè)簡(jiǎn)單的測(cè)試類來(lái)測(cè)試一下
public class SimpleTest { public static void main(String[] args){ SimpleObservable doc = new SimpleObservable (); SimpleObserver view = new SimpleObserver (doc); doc.setData(1); doc.setData(2); doc.setData(2); doc.setData(3); } }
運(yùn)行結(jié)果如下
Data has changed to 1
Data has changed to 2 //第二次setData(2)時(shí)由于沒有setChange,所以u(píng)pdate沒被調(diào)用
Data has changed to 3
下面介紹一個(gè)Observable類的其他一些屬性和方法
屬性 ——
// observers是一個(gè)List,保存著所有要通知的observer。
List<Observer> observers = new ArrayList<Observer>();
// changed是一個(gè)boolean型標(biāo)志位,標(biāo)志著數(shù)據(jù)是否改變了。
boolean changed = false;
方法 ——
// 添加一個(gè)Observer到列表observers中 public void addObserver(Observer observer) { if (observer == null) { throw new NullPointerException(); } synchronized (this) { if (!observers.contains(observer)) observers.add(observer); } } // 從列表observers中刪除一個(gè)observer public synchronized void deleteObserver(Observer observer) { observers.remove(observer); } // 清空列表observers public synchronized void deleteObservers() { observers.clear(); } // 返回列表observers中observer的個(gè)數(shù) public int countObservers() { return observers.size(); } // 重置數(shù)據(jù)改變標(biāo)志位為未改變 protected void clearChanged() { changed = false; } // 將數(shù)據(jù)改變標(biāo)志位設(shè)置為改變 protected void setChanged() { changed = true; } // 判斷標(biāo)志位的值 public boolean hasChanged() { return changed; } // 通知所有observer(無(wú)參) public void notifyObservers() { notifyObservers(null); } // 通知所有observer(有參) @SuppressWarnings("unchecked") public void notifyObservers(Object data) { int size = 0; Observer[] arrays = null; synchronized (this) { if (hasChanged()) { clearChanged(); size = observers.size(); arrays = new Observer[size]; observers.toArray(arrays); } } if (arrays != null) { for (Observer observer : arrays) { observer.update(this, data); } } }
注意:在Observer對(duì)象銷毀前一定要用deleteObserver將其從列表中刪除,也就是在onDestroy()方法中調(diào)用deleteObserver()方法。
不然因?yàn)檫€存在對(duì)象引用的關(guān)系,Observer對(duì)象不會(huì)被垃圾收集,造成內(nèi)存泄漏,并且已死的Observer仍會(huì)被通知到,有可能造成意料外的錯(cuò)誤,而且隨著列表越來(lái)越大,notifyObservers操作也會(huì)越來(lái)越慢。
觀察者模式所涉及的角色有:
● 抽象主題(Subject)角色:抽象主題角色把所有對(duì)觀察者對(duì)象的引用保存在一個(gè)聚集(比如ArrayList對(duì)象)里,每個(gè)主題都可以有任何數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者對(duì)象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
● 具體主題(ConcreteSubject)角色:將有關(guān)狀態(tài)存入具體觀察者對(duì)象;在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
● 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個(gè)接口,在得到主題的通知時(shí)更新自己,這個(gè)接口叫做更新接口。
● 具體觀察者(ConcreteObserver)角色:存儲(chǔ)與主題的狀態(tài)自恰的狀態(tài)。具體觀察者角色實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài) 像協(xié)調(diào)。如果需要,具體觀察者角色可以保持一個(gè)指向具體主題對(duì)象的引用。
相關(guān)文章
Spring Boot配置接口WebMvcConfigurer的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot配置接口WebMvcConfigurer的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11SpringBoot如何優(yōu)雅的處理校驗(yàn)參數(shù)的方法
這篇文章主要介紹了SpringBoot如何優(yōu)雅的處理校驗(yàn)參數(shù)的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12SpringBoot使用Feign進(jìn)行服務(wù)間通信的實(shí)現(xiàn)示例代碼
Feign是一個(gè)開源的Java HTTP客戶端,可以幫助我們?cè)赟pringBoot應(yīng)用中快速構(gòu)建和使用HTTP客戶端,方便實(shí)現(xiàn)服務(wù)間的通信,本文就來(lái)介紹一下SpringBoot使用Feign進(jìn)行服務(wù)間通信的實(shí)現(xiàn)示例代碼,感興趣的可以了解一下2024-01-01java分頁(yè)之假分頁(yè)實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)器
這篇文章主要介紹了java分頁(yè)之假分頁(yè)實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)器的相關(guān)資料,需要的朋友可以參考下2016-04-04springboot項(xiàng)目配置context path失效的問題解決
本文主要介紹了springboot項(xiàng)目配置context path失效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04基于hibernate實(shí)現(xiàn)的分頁(yè)技術(shù)實(shí)例分析
這篇文章主要介紹了基于hibernate實(shí)現(xiàn)的分頁(yè)技術(shù),結(jié)合實(shí)例形式分析了Hibernate分頁(yè)技術(shù)的原理,實(shí)現(xiàn)步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-03-03Apache?SkyWalking?修復(fù)TTL?timer?失效bug詳解
這篇文章主要為大家介紹了Apache?SkyWalking?修復(fù)TTL?timer?失效bug詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09SpringBoot雪花算法主鍵ID傳到前端后精度丟失問題的解決
本文主要介紹了SpringBoot雪花算法主鍵ID傳到前端后精度丟失問題的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08