天天看點

6.[研磨設計模式筆記]觀察者模式 1.定義 2.解決問題 3.模式講解 4.思考

定義對象間的一種一對多的依賴關系,當一個對象的狀态發生改變時,所有依賴于它的對象都得到通知并自動更新。

——訂閱報紙

<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&lt;Observer&gt; readers = </code><code>new</code> <code>ArrayList&lt;Observer&gt;();</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&lt;Observer&gt; observers = </code><code>new</code> <code>ArrayList&lt;Observer&gt;();</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,如需轉載請自行聯系原作者

繼續閱讀