定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象都得到通知并自動更新。
——訂閱報紙
<a target="_blank" href="http://blog.51cto.com/attachment/201306/181331406.png"></a>
看起來訂閱者是直接根有據打交道,但實際上,訂閱者的訂閱資料是被郵寄傳遞到報社,當報社出了報紙,報社按訂閱資訊交給郵局,郵局在代為發送到訂閱者手裡。在整個過程中,郵局起到一個中轉的作用。
使用觀察者模式來解決問題
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
<code>public</code> <code>class</code> <code>Subject {</code>
<code> </code><code>private</code> <code>List<Observer> readers = </code><code>new</code> <code>ArrayList<Observer>();</code>
<code> </code><code>public</code> <code>void</code> <code>attach(Observer reader) {</code>
<code> </code><code>readers.add(reader);</code>
<code> </code><code>}</code>
<code> </code><code>public</code> <code>void</code> <code>detach(Observer observer) {</code>
<code> </code><code>readers.remove(observer);</code>
<code> </code><code>protected</code> <code>void</code> <code>notifyObservers() {</code>
<code> </code><code>for</code> <code>(Observer reader : readers)</code>
<code> </code><code>reader.update(</code><code>this</code><code>);</code>
<code>}</code>
<code>public</code> <code>class</code> <code>NewsPaper </code><code>extends</code> <code>Subject {</code>
<code> </code><code>private</code> <code>String content;</code>
<code> </code><code>public</code> <code>String getContent() {</code>
<code> </code><code>return</code> <code>content;</code>
<code> </code><code>public</code> <code>void</code> <code>setContent(String content) {</code>
<code> </code><code>this</code><code>.content = content;</code>
<code> </code><code>super</code><code>.notifyObservers();</code>
<code>public</code> <code>interface</code> <code>Observer {</code>
<code> </code><code>public</code> <code>void</code> <code>update(Subject subject);</code>
<code>public</code> <code>class</code> <code>Reader </code><code>implements</code> <code>Observer {</code>
<code> </code><code>private</code> <code>String name;</code>
<code> </code><code>public</code> <code>void</code> <code>update(Subject subject) {</code>
<code> </code><code>name = ((NewsPaper)subject).getContent();</code>
<code> </code><code>System.out.println(name);</code>
<code>public</code> <code>class</code> <code>Client {</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
<code> </code><code>// 建立一個報紙,作為目标</code>
<code> </code><code>NewsPaper subject = </code><code>new</code> <code>NewsPaper();</code>
<code> </code><code>// 建立一個讀者</code>
<code> </code><code>Reader reader1 = </code><code>new</code> <code>Reader();</code>
<code> </code><code>Reader reader2 = </code><code>new</code> <code>Reader();</code>
<code> </code><code>// 注冊訂閱者</code>
<code> </code><code>subject.attach(reader1);</code>
<code> </code><code>subject.attach(reader2);</code>
<code> </code><code>// 要出報紙啦</code>
<code> </code><code>subject.setContent(</code><code>"New"</code><code>);</code>
上述問題中訂閱者最關心的問題時是何時能收到最新處的報紙,但要是報紙出版的時間不固定怎麼辦?進一步抽象這個問題:當一個對象的狀态發生改變的時候,如果讓依賴于它的所有對象得到通知,并進行相應的處理呢?
解決思路
這是一個典型一對多的對象關系,一個報紙對象,有很多個訂閱者對象來訂閱,當報紙出版的時候,也就是報紙對象改變的時候。觀察者模式處理這種問題時,把這多個訂閱者稱為觀察者:Observer,多個觀察者觀察的對象稱為目标:Subject。
一個目标可以有任意多個觀察者獨享,一旦目标的狀态發生改變,所有注冊的觀察者都會得到通知,然後各個觀察者會對通知做出相應的響應,執行相應的業務功能處理,并使自己的狀态與目标狀态保持一緻。
示例代碼
<code> </code><code>// 用來儲存注冊的觀察者對象</code>
<code> </code><code>private</code> <code>List<Observer> observers = </code><code>new</code> <code>ArrayList<Observer>();</code>
<code> </code><code>// 注冊觀察者對象</code>
<code> </code><code>public</code> <code>void</code> <code>attach(Observer observer) {</code>
<code> </code><code>observers.add(observer);</code>
<code> </code><code>// 删除觀察者對象</code>
<code> </code><code>observers.remove(observer);</code>
<code> </code><code>// 通知所有注冊的觀察者對象</code>
<code> </code><code>for</code> <code>(Observer observer : observers)</code>
<code> </code><code>observer.update(</code><code>this</code><code>);</code>
<code>public</code> <code>class</code> <code>ConcreteSubject </code><code>extends</code> <code>Subject {</code>
<code> </code><code>private</code> <code>String subjectState;</code>
<code> </code><code>public</code> <code>String getSubjectState() {</code>
<code> </code><code>return</code> <code>subjectState;</code>
<code> </code><code>public</code> <code>void</code> <code>setSubjectState(String subjectState) {</code>
<code> </code><code>this</code><code>.subjectState = subjectState;</code>
<code> </code><code>// 狀态改變了,通知各個觀察者</code>
<code> </code><code>this</code><code>.notifyObservers();</code>
<code>public</code> <code>class</code> <code>ConcreteObserver </code><code>implements</code> <code>Observer {</code>
<code> </code><code>private</code> <code>String observerState;</code>
<code> </code><code>// 可能需要更新觀察者狀态,使其與目标的狀态保持一緻</code>
<code> </code><code>observerState = ((ConcreteSubject) subject).getSubjectState();</code>
應用範圍
按照模式的定義,目标和觀察者之間是典型的一對多關系。但如果觀察者隻有一個,也是可以的;同樣,一個觀察者也可以觀察多個目标。
單向依賴
在觀察者模式中,觀察者和目标是單向依賴,隻有觀察者依賴于目标,而目标不能依賴與觀察者。
觀察者模式調用順序示意圖
在使用觀察者模式時,分為兩個階段:
第一個階段是準備階段,即維護目标和觀察者關系的階段;
<a target="_blank" href="http://blog.51cto.com/attachment/201306/181637861.png"></a>
第二階段是實際運作階段。
<a target="_blank" href="http://blog.51cto.com/attachment/201306/181644209.png"></a>
推模式和拉模式
推模式:目标對象主動向觀察者推送目标的詳細資訊,不管觀察者是否需要,推送的資訊通常是目标對象的全部或部分資料,相當于廣播通信。
拉模式:目标對象在通知觀察者的時候,隻傳遞少量資訊,如果觀擦或者需要更具體的資訊,由觀察者主動到目标對象中擷取,相當于觀察者從目标對象中拉資料。
Java觀察者模式
java.util包裡有一個Observable類,還有一個接口Observer,其中定義了update方法,就是觀察者接口。
<code>public</code> <code>class</code> <code>NewsPaper </code><code>extends</code> <code>java.util.Observable {</code>
<code> </code><code>// 注意在Java中Observer模式的時候,下邊這句不可少</code>
<code> </code><code>this</code><code>.setChanged();</code>
<code> </code><code>// 然後主動通知,這裡用的推模式</code>
<code> </code><code>this</code><code>.notifyObservers(</code><code>this</code><code>.content);</code>
<code>public</code> <code>class</code> <code>Reader </code><code>implements</code> <code>java.util.Observer {</code>
<code> </code><code>public</code> <code>void</code> <code>update(Observable o, Object obj) {</code>
<code> </code><code>// 這是采用推的方式</code>
<code> </code><code>System.out.println(name + obj);</code>
<code> </code><code>// 這是采用拉的方式</code>
<code> </code><code>System.out.println(name + ((NewsPaper) o).getContent());</code>
<code> </code><code>subject.addObserver(reader1);</code>
<code> </code><code>subject.addObserver(reader2);</code>
擴充卡模式的優缺點:
優點:實作了觀察者和目标之間的抽象耦合,實作了動态關聯,支援廣播通信
缺點:可能會引起無謂的操作
觀察者模式的本質是:觸發關聯
當修改目标對象的狀态時,就會觸發相應的通知,然後會循環調用所有注冊的觀擦或者對象的相應方法,其實就相當于關聯調用這些觀察者的方法。
何時選用擴充卡模式
當一個抽象模型有兩個方面,其中一個方面的操作依賴于另一個方面的狀态變化,可以選用觀察者模式;
如果在更改一個對象的時候,需要同時連帶改變其他的喜愛那個,而且不知道究竟應該有多少對象需要被連帶改變,可以選用觀察者模模式;
當一個對象必須通知其他對象,但是有希望這個對象和其他被通知的對象是松散耦合的,即這個對象其實不想知道具體被通知的對象,可以選用觀察者模式。
本文轉自 LinkedKeeper 51CTO部落格,原文連結:http://blog.51cto.com/sauron/1230190,如需轉載請自行聯系原作者