使用觀察者(Observer)實現(xiàn)對象監(jiān)聽
Bromon原創(chuàng) 請尊重版權(quán)
有非常多的時候,我們希望自己的程序能夠監(jiān)視數(shù)據(jù)的變化,然后做出響應(yīng),這種情況非常多,比如探測數(shù)據(jù)庫中數(shù)據(jù)的變化、檢測用戶狀態(tài)的變化等等。通常我們都缺乏一種雙工通信的機(jī)制,只能選擇讓程序做論詢,隔一段時間檢測一次數(shù)據(jù)變化,記錄下來與上一次檢測結(jié)果做對比,從而判斷數(shù)據(jù)是否發(fā)生了變化。毫無疑問這樣的方式很笨拙,不僅寫起來痛苦,跑起來也耗資源,是典型的用80%的時間解決20%的問題。
觀察者(Observer)是一種模式,也是Java中的一個API,它讓一個值對象(Value Object)具備自省的功能,當(dāng)他發(fā)現(xiàn)自己的狀態(tài)改變了,就向相關(guān)的對象發(fā)送消息,這樣的監(jiān)聽方式當(dāng)然比輪詢好。我感冒了自己會去醫(yī)院,用不著醫(yī)生每個月來問一次。禽獸·宇楓曾經(jīng)給了我一段麻將游戲的服務(wù)器端代碼,本來是讓我研究一下麻將的算法,但是卻被其中Observer的使用所吸引,這樣寫出來的服務(wù)器執(zhí)行效率很高。我曾經(jīng)用線程池+反射+觀察者寫了一個即時消息的服務(wù)器,既有socket的方便也具備udp的高效,可惜后來因為大幅修改設(shè)計代碼作廢了,不過觀察者還是值得研究。
Java的Observer API是對觀察者模式的一個實現(xiàn)。假設(shè)我們有一個對象容器,其中存放用戶消息,我希望這個容器自省,當(dāng)有新的消息進(jìn)來就自動觸發(fā)觀察者作出響應(yīng)。首先定義消息對象,是個很簡單的值對象:
然后寫一個存放Message的容器,容器使用ArrayList來存放對象是個很好的選擇,也很簡單:
這個類繼承了Observable類,并且對其中的add方法做了手腳,很明顯,add方法的作用是向ArrayList容器中放入一個對象,這正是我們想監(jiān)聽的操作,所以有了:
uper.setChanged();
super.notifyObservers(m);
這意思是一旦調(diào)用add方法,這個類自己就會向所有注冊過的觀察者發(fā)送消息,消息內(nèi)容是什么呢?內(nèi)容就是m,是存放消息的容器,觀察者可以收到這個改變了狀態(tài)的容器,然后對它進(jìn)行操作,從而實現(xiàn)了對容器的監(jiān)聽,當(dāng)然,我們只實現(xiàn)了對add方法的監(jiān)聽,你也可以試試其他的。
需要特別注意的是這是一個不完整的單例類,寫成單例是為了要保證整個jvm中只有這一個存放消息的容器,而不寫成完整的單例,原因是將來可能要提供另外的實例化方法。所以理解起來可能稍微難一點,大家可以參考一下設(shè)計模式中的單例模式。
下面就是編寫觀察者并且注冊它:
這個類繼承Oberver接口,update(Observable,Object)是必須提供的方法,在這個方法中我們接收被觀察類傳過來的數(shù)據(jù)(含有消息的容器),然后取出其中最后一個,讀取它的內(nèi)容。
Java里的觀察者使用起來是非常簡單的。我們的例子好處是所有的操作都在內(nèi)存中進(jìn)行,而且不需要輪詢,效率非常高,缺點是一旦當(dāng)機(jī)內(nèi)存中的數(shù)據(jù)就丟失了,所以如果有一套比較完善的對象緩沖機(jī)制,就可以應(yīng)付復(fù)雜的應(yīng)用,寫出高效簡潔的多線程服務(wù)器。