JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Software Design » GoF  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 Design Patterns 入門 - 觀察者(Observer) [精華]
caterpillar

良葛格

版主

發文: 2613
積分: 70
於 2003-12-25 23:53 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
此文件已有新版
http://openhome.cc/Gossip/DesignPattern/

假設今天您設計一個試算表程式,當中有一個資料物件,您可以用表格圖形物件、柱狀圖形物件、圓餅圖形物件等方式來呈現物件,無論您是用哪種圖形物件,重點是若資料物件的內容作了更改,則圖形物件的內容也必須跟著修改,或許您的程式中有兩個以上的圖形物件來呈現資料,您在圖形物件上更動資料,則另一個圖形物件也必須作出相對應的變化。

1
2
3
4
5
主題     觀察者
 
     <--> 柱狀圖形
資料物件 <--> 表格圖形
     <--> 圓餅圖形


又假設您今天設計一個網路遊戲,您在伺服器上維護一個連線客戶端共享的資料物件,當其中一個客戶端作了操作,將對此資料物件作修改,則伺服器必須通知其它客戶端作相對應的變化(像是人物位置走動、建了一個城堡等)。

1
2
3
4
5
主題     觀察者
 
     <--> 客戶端圖形介面一
資料物件 <--> 客戶端圖形介面二
     <--> 客戶端圖形介面三


在觀察者(Observer)模式中的主角為主題(subject)與觀察者(observer),觀察者訂閱它感興趣的主題,一個主題可以被多個觀察者訂閱,當主題的狀態發生變化時,它必須通知(notify)所有訂閱它的觀察者,觀察者檢視主題的狀態變化,並作出對應的動作,所以觀察者模式也稱之為「發佈-訂閱」(Publish-Subscribe)模式。

觀察者模式的UML圖如下所示:


Subject類中有一個notify()方法,通常是在Subject的狀態發生改變時呼叫它,notify()中會呼叫Observer的update()方法,通常會先取得Subject的新狀態,然後更新Observer的顯示或行為,這個過程我們可以透過順序圖(Sequence Diagram)來表達:


在Java中支援觀察者模式,要成為觀察者的類必須實作Observer介面,這個介面中定義了一個update()方法,這個方法會被主題物件在通知狀態變化時呼叫,您必須在這個方法中實作您所想要的對應行為。

主題物件會是Observable的子類,在這邊注意兩個重要的方法:setChanged()與notifyObserver()。setChanged()是用來設定主題物件的狀態已經被改變,而notifyObserver()方法會通知所要訂閱主題物件的觀察者,調用其update()方法。

有興趣的話,建議看一下Observable.java中是如何實作的,這有助於瞭解觀察者模式的運作方式,以下列出Observable.java的內容以方便參考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
 * @(#)Observable.java  1.35 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
 
package java.util;
 
/**
 * This class represents an observable object, or "data"
 * in the model-view paradigm. It can be subclassed to represent an 
 * object that the application wants to have observed. 
 * <p>
 * An observable object can have one or more observers. An observer 
 * may be any object that implements interface <tt>Observer</tt>. After an 
 * observable instance changes, an application calling the 
 * <code>Observable</code>'s <code>notifyObservers</code> method  
 * causes all of its observers to be notified of the change by a call 
 * to their <code>update</code> method. 
 * <p>
 * The order in which notifications will be delivered is unspecified.  
 * The default implementation provided in the Observerable class will 
 * notify Observers in the order in which they registered interest, but 
 * subclasses may change this order, use no guaranteed order, deliver 
 * notifications on separate threads, or may guarantee that their
 * subclass follows this order, as they choose.
 * <p>
 * Note that this notification mechanism is has nothing to do with threads 
 * and is completely separate from the <tt>wait</tt> and <tt>notify</tt> 
 * mechanism of class <tt>Object</tt>.
 * <p>
 * When an observable object is newly created, its set of observers is 
 * empty. Two observers are considered the same if and only if the 
 * <tt>equals</tt> method returns true for them.
 *
 * @author  Chris Warth
 * @version 1.35, 01/23/03
 * @see     java.util.Observable#notifyObservers()
 * @see     java.util.Observable#notifyObservers(java.lang.Object)
 * @see     java.util.Observer
 * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
 * @since   JDK1.0
 */
public class Observable {
    private boolean changed = false;
    private Vector obs;
   
    /** Construct an Observable with zero Observers. */
 
    public Observable() {
  obs = new Vector();
    }
 
    /**
     * Adds an observer to the set of observers for this object, provided 
     * that it is not the same as some observer already in the set. 
     * The order in which notifications will be delivered to multiple 
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
  if (!obs.contains(o)) {
      obs.addElement(o);
  }
    }
 
    /**
     * Deletes an observer from the set of observers of this object. 
     *
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
 
    /**
     * If this object has changed, as indicated by the 
     * <code>hasChanged</code> method, then notify all of its observers 
     * and then call the <code>clearChanged</code> method to 
     * indicate that this object has no longer changed. 
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and <code>null</code>. In other 
     * words, this method is equivalent to:
     * <blockquote><tt>
     * notifyObservers(null)</tt></blockquote>
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
  notifyObservers(null);
    }
 
    /**
     * If this object has changed, as indicated by the 
     * <code>hasChanged</code> method, then notify all of its observers 
     * and then call the <code>clearChanged</code> method to indicate 
     * that this object has no longer changed. 
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
  /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;
 
  synchronized (this) {
      /* We don't want the Observer doing callbacks into
       * arbitrary code while holding its own Monitor.
       * The code where we extract each Observable from 
       * the Vector and store the state of the Observer
       * needs synchronization, but notifying observers
       * does not (should not).  The worst result of any 
       * potential race-condition here is that:
       * 1) a newly-added Observer will miss a
       *   notification in progress
       * 2) a recently unregistered Observer will be
       *   wrongly notified when it doesn't care
       */
      if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
 
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
 
    /**
     * Clears the observer list so that this object no longer has any observers.
     */
    public synchronized void deleteObservers() {
  obs.removeAllElements();
    }
 
    /**
     * Marks this <tt>Observable</tt> object as having been changed; the 
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
  changed = true;
    }
 
    /**
     * Indicates that this object has no longer changed, or that it has 
     * already notified all of its observers of its most recent change, 
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>. 
     * This method is called automatically by the 
     * <code>notifyObservers</code> methods. 
     *
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     */
    protected synchronized void clearChanged() {
  changed = false;
    }
 
    /**
     * Tests if this object has changed. 
     *
     * @return  <code>true</code> if and only if the <code>setChanged</code> 
     *          method has been called more recently than the 
     *          <code>clearChanged</code> method on this object; 
     *          <code>false</code> otherwise.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#setChanged()
     */
    public synchronized boolean hasChanged() {
  return changed;
    }
 
    /**
     * Returns the number of observers of this <tt>Observable</tt> object.
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
  return obs.size();
    }
}


caterpillar edited on 2013-04-07 16:03
reply to postreply to post
良葛格學習筆記
作者 Re:Design Patterns - 觀察者(Observer) [Re:caterpillar]
popcorny

Jakarta 2%

版主

發文: 752
積分: 20
於 2003-12-29 22:02 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
現在在java中
好像
Listener event event-source
這種model比較常見到耶...
listener: java.util.EventListener
event: java.util.Event
source: Object
可能是把event包裝成Event的方式比較好用吧...
好像好少看到有程式用到Observable, Observer的說...


reply to postreply to post
作者 Re:Design Patterns - 觀察者(Observer) [Re:caterpillar]
popcorny

Jakarta 2%

版主

發文: 752
積分: 20
於 2003-12-29 22:08 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
對了caterpillar兄
很抱歉之前都沒有給你加分
你的這些DP的文章對初學DP的網友非常有用
絕對是有加分的價值
可是一篇加一分有點太多
大家大概看了會眼紅...Embaressed
而且積分榜大概馬上就會遙遙領銜了吧
所以以後這系列每po兩篇就幫你加一分好了...
希望你能把剩下部分都全部補齊...Smile


reply to postreply to post
作者 Re:Design Patterns - 觀察者(Observer) [Re:popcorny]
caterpillar

良葛格

版主

發文: 2613
積分: 70
於 2003-12-29 23:21 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
popcorny wrote:
現在在java中
好像
Listener event event-source
這種model比較常見到耶...
listener: java.util.EventListener
event: java.util.Event
source: Object
可能是把event包裝成Event的方式比較好用吧...
好像好少看到有程式用到Observable, Observer的說...


在Applying UML and Patterns中有寫到,在Java中事實上存在的是這種設計的變種,不需要一個顯示的Callback類。介面與事件指派模型就可以滿足這種需求。

這種作法好像各有利弊,我是沒有特別去比較過啦。。。。所以直接引用大師的話囉。。。Tongue


reply to postreply to post
良葛格學習筆記
作者 Re:Design Patterns - 觀察者(Observer) [Re:popcorny]
caterpillar

良葛格

版主

發文: 2613
積分: 70
於 2003-12-29 23:29 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
popcorny wrote:
對了caterpillar兄
很抱歉之前都沒有給你加分
你的這些DP的文章對初學DP的網友非常有用
絕對是有加分的價值
可是一篇加一分有點太多
大家大概看了會眼紅...Embaressed
而且積分榜大概馬上就會遙遙領銜了吧
所以以後這系列每po兩篇就幫你加一分好了...
希望你能把剩下部分都全部補齊...Smile


哈!我不是來要糖吃的啦!

事實上我接觸模式也才是這兩個月的事,閉門造車一定會有觀念錯誤的地方,寫出來讓大家看看,接受一下批評,對我的學習會有很大的幫助的,這是主要的目的。。。。

不用特意幫我作加分的動作,有錯的話儘量幫我指正就好了。。。

基本上會寫的主題有Gof的23種模式,寫文件的發表時間依我的時間、理解與收集到的資料而長短不一,尤其是後兩者,有一些東西我一時還無法理解,需要收集較多的資料消化才寫的出來。。。。可以確定的是一定會寫就是了。。。寫的順序不一定依照Gof的編排,我先理解那個就先寫那個,有時也許插入一些額外的模式,當然我只是個引子,真正有內容的還是Gof的書,每看一次就會被啟發一次。。。。

也許以後會繼續寫一些其它模式,像是多執行緒我就很有興趣。。。。


reply to postreply to post
良葛格學習筆記
作者 Re:Design Patterns - 觀察者(Observer) [Re:caterpillar]
popcorny

Jakarta 2%

版主

發文: 752
積分: 20
於 2003-12-30 00:56 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
caterpillar wrote:
哈!我不是來要糖吃的啦!
事實上我接觸模式也才是這兩個月的事,閉門造車一定會有觀念錯誤的地方,寫出來讓大家看看,接受一下批評,對我的學習會有很大的幫助的,這是主要的目的。。。。
不用特意幫我作加分的動作,有錯的話儘量幫我指正就好了。。。

你寫的都沒什麼問題...
我還希望偶而有點錯..
這樣就可以討論了...XD


基本上會寫的主題有Gof的23種模式,寫文件的發表時間依我的時間、理解與收集到的資料而長短不一,尤其是後兩者,有一些東西我一時還無法理解,需要收集較多的資料消化才寫的出來。。。。可以確定的是一定會寫就是了。。。寫的順序不一定依照Gof的編排,我先理解那個就先寫那個,有時也許插入一些額外的模式,當然我只是個引子,真正有內容的還是Gof的書,每看一次就會被啟發一次。。。。
也許以後會繼續寫一些其它模式,像是多執行緒我就很有興趣。。。。

如果有問題的話也歡迎提出來...
直接在這邊問也比較快
而且可能你遇到的問題就是大家會遇到的問題.
感謝caterpillar的分享
我會當忠實讀者...


reply to postreply to post
作者 Re:Design Patterns 入門 - 觀察者(Observer) [Re:caterpillar]
GuanChih

ASKA



發文: 243
積分: 2
於 2006-02-10 10:37 user profilesend a private message to usersend email to GuanChihreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
如果繼承 Observer 的子類,在addObserver的時候,
利用Spring 的 DI 將訂閱者由外部設定,
這種應用的方式是好的嗎?

再者,若訂閱者無法由xml來指定,
例如訂閱者的類別會因某些條件而定,
而必須依條件去資料庫撈取(動態)
這樣的狀況,若仍想由Spring來完成,是否就無法用DI了?

還是上述的想法,不是DI的適用範圍?


reply to postreply to post
作者 Re:Design Patterns 入門 - 觀察者(Observer) [Re:GuanChih]
tempo



版主

發文: 645
積分: 7
於 2006-02-10 18:20 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
GuanChih wrote:
如果繼承 Observer 的子類,在addObserver的時候,
利用Spring 的 DI 將訂閱者由外部設定,
這種應用的方式是好的嗎?

還不錯呀~

再者,若訂閱者無法由xml來指定,
例如訂閱者的類別會因某些條件而定,
而必須依條件去資料庫撈取(動態)
這樣的狀況,若仍想由Spring來完成,是否就無法用DI了?

不能用像 spring xml 這種方式來設定, 並不代表就不是 DI..
DI 只是使用者不需要知道所需傳入物件的來源與生成方式..

那回到你上面的例子, observer 的列表是資料庫中撈取出來,
這個狀況我覺得也可以使用 builder pattern 把撈的動作獨立出來..
然後再將 builder 傳入需要的物件中..


reply to postreply to post
Welcome to: gamelet.com
and my blog: www.pocketshark.com/blog/page/tempo
» JWorld@TW »  Software Design » GoF

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8