1.什么是觀察者模式 觀察者模式是軟件設(shè)計模式的一種。在此種模式中,一個目標(biāo)物件管理所有相依于它的觀察者物件,并且在它本身的狀態(tài)改變時主動發(fā)出通知。 2.觀察者模式的實現(xiàn) 雖然在Java中提供了現(xiàn)成的Observer和Observable實現(xiàn),但由于Observable已經(jīng)被實現(xiàn)成class,所以當(dāng)需要將自己的某個業(yè)務(wù)類實現(xiàn)成可被觀察的特性時,往往還是得自己來實現(xiàn),因此下文不再使用Java中的Observable和Observer類,而是自行實現(xiàn)。 通常的實現(xiàn)方式是注冊-通知-取消注冊。代碼如下: - public interface Listener {
- void onNotify();
- }
-
-
- public class Subject {
- private List<Listener> listeners;
- private Object value;
-
- public Subject(){
- listeners=new ArrayList<Listener>();
- value=new Object();
- }
-
- public void registe(Listener listener){
- listeners.add(listener);
- }
-
- public void unRegiste(Listener listener){
- listeners.remove(listener);
- }
-
- private void sendNotify(){
- for(Listener listener:listeners){
- listener.onNotify();
- }
- }
-
- public Object getValue(){
- return value;
- }
-
- public void setValue(Object value){
- if (!this.value.equals(value)){
- this.value=value;
- sendNotify();
- }
- }
- }
2.1.容器的選擇 被觀察者需要一個容器來保留注冊進(jìn)來的觀察者引用,上文的代碼中使用了List作為容器,這個容器的選擇會影響到整體的實現(xiàn)方案。 通常選擇用一個Array或List容器來存儲,這樣一方面可以做到有序,另一方面在通知時方便遍歷。 如果不想有重復(fù)的觀察者引用,則要么在注冊時檢查是否已經(jīng)存在于容器中,比如在上文registe()方法中加入contains()的判斷,要么就選擇不可重復(fù)的容器如Set,Map等。這樣的容器本身已經(jīng)包含了對重復(fù)的處理,但也意味著在發(fā)送通知時的順序不是注冊時的先后順序。 2.2.通知后的數(shù)據(jù)獲取方式 也就是常說的‘推’或‘拉’。推方式是說變化的數(shù)據(jù)隨著對觀察者的接口調(diào)用以參數(shù)的方式傳遞給觀察者。拉方式是說對觀察者的接口調(diào)用只用于通知數(shù)據(jù)發(fā)生了變化這件事情,由觀察者自行調(diào)用被觀察者的getXXX()方法取得感興趣的數(shù)據(jù)。前者不管三七二十一把變化就推給觀察者了,不管你有用沒用;后者只是盡到通知的義務(wù),你對什么數(shù)據(jù)感興趣就自己來拿。這個要根據(jù)實際的設(shè)計情況進(jìn)行決策。 上文的代碼中使用了拉的方式,當(dāng)value發(fā)生變化時,會通過sendNotify()方法發(fā)送通知給Listener,但通知中未攜帶value的值,而是由Listener一方根據(jù)情況調(diào)用getValue()方法來拉取變化后的新的value值。 2.3.通知后的取消注冊 通知后的取消在實際應(yīng)用中也沒有上述示例代碼這樣簡單。一個典型的需求就是某個Listener要在onNotify()方法執(zhí)行的過程中調(diào)用unRegiste(this)。對這一需求的滿足可以小心的處理sendNotify()方法,也可以通過標(biāo)識位進(jìn)行標(biāo)識,還可以在sendNotify()中不使用原始的容器而是遍歷一個副本,無論怎樣,這個需求很容易發(fā)生,在實現(xiàn)觀察者模式時要進(jìn)行處理。 2.4.其他問題 如果Subject是時間相關(guān)的業(yè)務(wù),就要對每一個listener的運行時間加以約束,要能對每一個onNotify的調(diào)用時間進(jìn)行監(jiān)控并處理,以保持對每一個Listener的時間分配在一個合理的范圍內(nèi)。或者使用異步、多線程方式調(diào)起onNotify()。 如果Subject需要實現(xiàn)按優(yōu)先級進(jìn)行通知,那就要在優(yōu)先級隊列管理及通知調(diào)度上下功夫了。 3.總結(jié) 觀察者模式說得多,用得多,但在實際工作當(dāng)中,要深入思考而不是完全照搬示例代碼,這也是設(shè)計模式給我們的,要學(xué)會把它變成自己的經(jīng)驗。
|