?? 今天有同事問我關(guān)于Observer模式的一個(gè)問題,說觀察者(Observer)為什么要依賴于主題(Subject),如下圖所示:
?
??? 從上圖可以看出,具體的觀察者對(duì)具體的主題有一個(gè)依賴, 而且從JDK本身提供的Observer接口,我們也可以看到,確實(shí)對(duì)主題(在JDK的實(shí)現(xiàn)中,為Observable接口)有一個(gè)依賴,如代碼所示:
?
public interface Observer { void update(Observable o, Object arg); }
??
?
?? 這個(gè)是為什么呢,為什么這里會(huì)依賴主題對(duì)象?其實(shí)這個(gè)跟Subject與Observer之間通訊的方式有關(guān),當(dāng)Subject本身狀態(tài)發(fā)生變化時(shí),會(huì)去通知注冊(cè)了的Observer(即調(diào)用每個(gè)observer的update()方法),那么這個(gè)時(shí)候,主題本身要怎么樣去給Observer傳輸其需要的數(shù)據(jù)呢?
??
?? 在我們看到的大多數(shù)觀察者的實(shí)現(xiàn)中,主題(Subject)在通知注冊(cè)的Observer時(shí),都會(huì)把Observer所需要的數(shù)據(jù)封裝好,傳給Observer,這個(gè)也就是所謂的‘推’的模式,主題主動(dòng)將數(shù)據(jù)推給觀察者,這種情況下,Observer的接口往往定義如下:
??????
public interface Observer { void update(Object arg);//從主題傳入的數(shù)據(jù) }
?
?? 在這種‘推’的模式下,觀察者本身是不依賴于主題對(duì)象的。 但還有另外一種所謂‘拉’的通訊方式,是指觀察者在需要數(shù)據(jù)的時(shí)候主動(dòng)從主題對(duì)象中獲取,這個(gè)情況下面就會(huì)出現(xiàn)觀察者依賴于主題對(duì)象,
???
public interface Observer { void update(Subject subject); }
?
??? 由于這種拉的實(shí)現(xiàn)方法出現(xiàn)的比較少,而且‘拉’數(shù)據(jù)的模式有一個(gè)比較大的缺點(diǎn),那就是出現(xiàn)了主題對(duì)象和觀察者對(duì)象之間的循環(huán)依賴,處理不好則很容易出來死循環(huán)。
???
???? 但是對(duì)于一個(gè)完整的觀察者模式來說,這兩種數(shù)據(jù)傳輸?shù)姆绞蕉际切枰模@也就是JDK的Observer接口中的update()方法要有兩個(gè)參數(shù)的原因(Obervable對(duì)象一般對(duì)應(yīng)于拉模式,Object對(duì)象一般對(duì)應(yīng)于推模式),如果你做過Swing編程,你會(huì)發(fā)現(xiàn)在Swing的事件處理中,listner(實(shí)際上就是Observer)所接受的參數(shù)也支持推拉兩種數(shù)據(jù)方式,如
?????
public interface MouseListener extends EventListener { public void mouseClicked(MouseEvent e); 。。。其他略 }
??? 這里的MouseEvent對(duì)象實(shí)際上也包含了數(shù)據(jù)來源對(duì)象(觸發(fā)事件的對(duì)象),即具體主題對(duì)象,而除了主題對(duì)象之外的其他屬性,我們都可以看成是推模式中所傳的數(shù)據(jù)。
?
???? 好了,解決了同事了疑問,還需要點(diǎn)明Observer模式的另一職責(zé)。由于我們大多數(shù)的Observer模式的實(shí)現(xiàn)都很簡單,在這樣的實(shí)現(xiàn)下,主題對(duì)象大多只擁有一個(gè)職責(zé),那就是管理Observer的職責(zé)(包括通知Observer),
????
class ConcreteSubject implement ISubject { private List observers=.. public void addObserver(Observer obs) { //add observer } public void removeObserver(Observer obs) { //remove observer } public void notifyObservers(Object obj) { //notify observers } }
?
??? 加上Observer模式是為了解決一對(duì)多的關(guān)系,久而久之,導(dǎo)致大多數(shù)人都忘記了主題對(duì)象(Subject)本身還應(yīng)該有另一個(gè)職責(zé),管理Observer只是主題(Subject)對(duì)象應(yīng)有的共同的職責(zé),不要忘了,還有多主題對(duì)象這么一回事。舉個(gè)以前看到的例子,
??? 假設(shè)我們的主題對(duì)象需要從遠(yuǎn)程獲取一些數(shù)據(jù),并分別的將其記錄在DB中,和顯示在Screen上,那么套用Observer模式,可以表示為:
????
?
?? 其中,DBObserver將拿到的數(shù)據(jù)寫到DB中,而DisplayObserver將拿到的數(shù)據(jù)顯示在SCREEN上,而MessagesSubject則有了兩個(gè)職責(zé),一個(gè)是我們前面說的管理Observer的職責(zé),另一個(gè)是去遠(yuǎn)程取數(shù)據(jù)的職責(zé),而這個(gè)我認(rèn)為才是主題對(duì)象(Subject)應(yīng)該有的具體的職責(zé)。
??
public class MessagesSubject extends AbstractSubject implements Runnable { //管理Observer的職責(zé)會(huì)從AbstractSuject中獲得 //真正的業(yè)務(wù)邏輯 public void run() { //從遠(yuǎn)程獲得messages //通知觀察者 }
?
??? 完成一個(gè)完整的Observer模式很難,考慮的東西比較多(光是通知Observer這部分就有幾種不同的實(shí)現(xiàn)方式),不推薦每次都需要實(shí)現(xiàn)一個(gè)很完整的Observer模式,但是我們不應(yīng)該遺忘這些構(gòu)成完整Observer模式鎖需要的部分。
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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