JDK對觀察者模式的支持主要是通過Observable類和Observer接口。?
?
繼承Observable類表示“主題”角色,實現Observer接口表示觀察者。?
?
Observer的簡單功能介紹?
update(Observable o, Object arg) Observable唯一的方法,在被通知時被?
Observable調用.o表示主題對象,arg表示通知信息。由此看出這是一個“推-拉結合”使用的方式。arg不傳值就是拉模式,傳值就是推模式,當然也可以推一部分,拉一部分。?
?
Observable的簡單功能介紹:?
notifyObservers() 如果hasChanged()=ture,通知所有的觀察著(則調用Observer.update()方法)?
notifyObservers(Object arg) 如果hasChanged()=ture,通知所有的觀察著(則調 用Observer.update()方法),并把參數arg傳過去?
說明:notifyObservers()內部其實是notifyObservers(null);notifyObservers(Object arg)內部其實是 update(this,arg)和Observer中的 update方法對應。?
注意點:調用notifyObservers方法之前一定要先調用setChanged();方法。?
?
JDK中Observable類和Observer接口實現:
1.抽象主題
java.util.Observable
?
2.具體主題
import java.util.Observable; public class ConcreteSubject extends Observable { public void doBusiness(Object obj) { super.setChanged(); super.notifyObservers(obj); } }
?
3.抽象觀察者
java.util.Observer
?
4.具體觀察者
import java.util.Observable; import java.util.Observer; public class ConcreteObserverA implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("A Subject:" + o.getClass().getName() + " Object: " + arg.getClass().getName()); } } import java.util.Observable; import java.util.Observer; public class ConcreteObserverB implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("B Subject:" + o.getClass().getName() + " Object: " + arg.getClass().getName()); } }
?
?
5.測試調用
import java.util.Observer; public class Test { public static void main(String[] args) { ConcreteSubject observable = new ConcreteSubject(); Observer observer1 = new ConcreteObserverA(); Observer observer2 = new ConcreteObserverB(); observable.addObserver(observer1); observable.addObserver(observer2); observable.doBusiness(observer2); } }
?
?
?
?
?
?
?
?
?
通俗的說,觀察者模式定義了一個一對多的依賴關系,讓一個或多個觀察者對象監察一個主題對象,這樣一個主題對象在狀態上的變化就能夠通知所有依賴于此對象的觀察者對象,使得這些觀察者對象能夠自動更新。經典的例子就是GUI界面編程模式中的監聽模式。?
觀察者模式(Observer)完美的將觀察者和被觀察的對象分離開。比如說,用戶界面可以看作一個觀察者,業務數據被觀察者,用戶界面觀察業務數據的變化,發現數據變化后,就顯示最新動態在界面上。
觀察者(Observer)模式有時有稱為發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-收聽者(Source/Listener)模式或者從屬者(Dependents)模式。
?
?
?
?
?
推模型和拉模型?
推模型:目標對象主動向觀察者推送目標的詳細信息,不管觀察者是否需要,推送的信息通常是目 標對象的全部或部分數據,相當于是在廣播通信。實現方法就是,update 方法加一個參數,通過這個參數把數據傳過去。?
拉模型:目標對象在通知觀察者的時候,只傳遞少量信息,如果觀察者需要更具體的信息,由觀察 者主動到目標對象中獲取,相當于是觀察者從目標對象中拉數據。 一般這種模型的實現中,會把目標對象自身通過update方法傳遞給觀察者,這樣在觀察者 需要獲取數據的時候,就可以通過這個引用來獲取了。?
?
?
觀察者模式的優缺點?
1.觀察者模式實現了觀察者和目標之間的抽象耦合:原本目標對象在狀態發生改變的時候,需要直接調用所有的觀察者對象,但是抽象出觀察者接口過后,目標和觀察者就只是在抽象層面上耦合了,也就是說目標只是知道觀察者接口,并不 知道具體的觀察者的類,從而實現目標類和具體的觀察者類之間解耦。?
2.觀察者模式實現了動態聯動:所謂聯動,就是做一個操作會引起其它相關的操作。由于觀察者模式對觀察者注冊實行管 理,那就可以在運行期間,通過動態的控制注冊的觀察者,來控制某個動作的聯動范圍,從而 實現動態聯動。?
?
?
觀察者模式支持廣播通信?
由于目標發送通知給觀察者是面向所有注冊的觀察者,所以每次目標通知的信息就要對所有注冊的觀察者進行廣播。當然,也可以通過在目標上添加新的功能來限制廣播的范圍。?
在廣播通信的時候要注意一個問題,就是相互廣播造成死循環的問題。比如A和B兩個對象互為觀察者和目標對象,A對象發生狀態變化,然后A來廣播信息,B對象接收到通知后,在處理過程中,使得B對象的狀態也發生了改變,然后B來廣播信息,然后A對象接到通知后,又觸 發廣播信息……,如此A引起B變化,B又引起A變化,從而一直相互廣播信息,就造成死循環了。?
?
?
觀察者模式可能會引起無謂的操作,降低性能?
由于觀察者模式每次都是廣播通信,不管觀察者需不需要,每個觀察者都會被調用update方法,如果觀察者不需要執行相應處理,那么這次操作就浪費了。?
?
增加了復雜度,容易引起誤操作。死循環等。
?
?
?
?
觀察者模式的效果有以下的優點:?
實現了表示層和數據邏輯層的分離,并定義了穩定的更新消息傳遞機制,類別清晰,并抽象了更新接口,使得可以有各種各樣不同的表示層(觀察者)。?
Subject在發送廣播通知的時候,無須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。
觀察者模式支持廣播通訊。被觀察者會向所有的登記過的觀察者發出通知。?
遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。?
Subject和Observer之間是松偶合的,分別可以各自獨立改變。觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體觀察者列表,每一個具體觀察者都符合一個抽象觀察者的接口。被觀察者并不認識任何一個具體觀察者,它只知道它們都有一個共同的接口。 由于被觀察者和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。如果被觀察者和觀察者都被扔到一起,那么這個對象必然跨越抽象化和具體化層次。 ?
?
?
觀察者模式有下面的缺點:?
第一、如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。?
第二、如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察者模式是要特別注意這一點。?
第三、如果對觀察者的通知是通過另外的線程進行異步投遞的話,系統必須保證投遞是以自恰的方式進行的。?
第四、雖然觀察者模式可以隨時使觀察者知道所觀察的對象發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的對象是怎么發生變化的。?
?
?
?
?
?
觀察者模式的構造?
1.抽象主題(Subject)角色:
目標角色知道它的觀察者,可以有任意多個觀察者觀察同一個目標。?
? ? 并且提供注冊和刪除觀察者對象的接口。?
? ? 目標角色往往由抽象類或者接口來實現。?
主題角色把所有對視察者對象的援用保留在一個湊集(List)中,每個主題都能夠有任何數目的觀察者。抽象主題供給一個接口,可以增添跟刪除察看者對象,主題角色又叫做形象被觀察著角色,個別用一個抽象類或者一個接口實現?
2.抽象觀察者(Observer)角色:
為那些在目標發生改變時需要獲得通知的對象定義一個更新接口。?
? ? 抽象觀察者角色主要由抽象類或者接口來實現。?
為所有具體觀察者定義一個接口,在得到主題的通知時更新自己。這個接口又叫做更新借口。抽象觀察者角色普通用一個抽象類或一個接口實現。?
3.具體主題(ConcreteSubject)角色:
將有關狀態存入各個Concrete Observer對象。?
? ? 當它的狀態發生改變時, 向它的各個觀察者發出通知。?
將有關狀態存入具體觀察者對象,在具體的主題內部狀態轉變時,給所有登記過的觀察者發出告訴。具體主題角色又叫做具體被觀察著角色。?
4.具體觀察者(ConcreteObserver)角色:
存儲有關狀態,這些狀態應與目標的狀態保持一致。?
? ? 實現Observer的更新接口以使自身狀態與目標的狀態保持一致。?
? ? 在本角色內也可以維護一個指向Concrete Subject對象的引用。?
? ? 存儲于主題狀態自恰的狀態。詳細觀察者角色實現抽象觀察者角色所請求更新接口,以便使自身的狀況與主題的狀態相和諧 。
?
?
被觀察者接口應當有以下幾個要素:
1、一份觀察者的清單,來控制觀察者。
2、增刪方法,對清單進行操作。以及一個通知觀察者的方法,通過調用觀察者的update方法。
?
觀察者接口應當有以下幾個要素:
1、update方法,用于維系和被觀察者之間的聯系。?
?
?
?
?
自定的實現:
1.抽象主題
public interface AbstractSubject { public void addWatcher(AbstractWatcher watcher); public void removeWatcher(AbstractWatcher watcher); public void removeAllWatchers(); public void notifyAllWatahcers(); }
?
2.抽象觀察者
public interface AbstractWatcher { public void update(); }
?
3.具體主題
import java.util.ArrayList; import java.util.List; public class ConcreteSubject implements AbstractSubject { private List<AbstractWatcher> watcherList = new ArrayList<AbstractWatcher>(); @Override public void addWatcher(AbstractWatcher watcher) { this.watcherList.add(watcher); } @Override public void removeWatcher(AbstractWatcher watcher) { this.watcherList.remove(watcher); } @Override public void removeAllWatchers() { this.watcherList.clear(); } @Override public void notifyAllWatahcers() { for (AbstractWatcher watcher : this.watcherList) { watcher.update(); } } }
?
4.具體觀察者
public class ConcreteWatcherA implements AbstractWatcher { @Override public void update() { System.out.println("update A ..."); } } public class ConcreteWatcherB implements AbstractWatcher { @Override public void update() { System.out.println("update B ..."); } } public class ConcreteWatcherC implements AbstractWatcher { @Override public void update() { System.out.println("update C ..."); } }
?
?
5.調用測試
public class Test { public static void main(String[] args) { AbstractSubject subject = new ConcreteSubject(); AbstractWatcher watcher1 = new ConcreteWatcherA(); AbstractWatcher watcher2 = new ConcreteWatcherB(); AbstractWatcher watcher3 = new ConcreteWatcherC(); subject.addWatcher(watcher1); subject.addWatcher(watcher2); subject.addWatcher(watcher3); subject.notifyAllWatahcers(); } }
?
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
