免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
觀察者模式
                                                                        2002-06-03· ·閻宏 ··yesky
簡(jiǎn)單地說(shuō),觀察者模式定義了一個(gè)一對(duì)多的依賴(lài)關(guān)系,讓一個(gè)或多個(gè)觀察者對(duì)象監(jiān)察一個(gè)主題對(duì)象。這樣一個(gè)主題對(duì)象在狀態(tài)上的變化能夠通知所有的依賴(lài)于此對(duì)象的那些觀察者對(duì)象,使這些觀察者對(duì)象能夠自動(dòng)更新。

  觀察者模式的結(jié)構(gòu)

  觀察者(Observer)模式是對(duì)象的行為型模式,又叫做發(fā)表-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-收聽(tīng)者(Source/Listener)模式或從屬者(Dependents)模式。

  本模式的類(lèi)圖結(jié)構(gòu)如下:


圖1、觀察者模式的靜態(tài)結(jié)構(gòu)可從類(lèi)圖中看清楚。

  在觀察者模式里有如下的角色:

  . 抽象主題(Subject)角色:主題角色把所有的觀察者對(duì)象的引用保存在一個(gè)列表里;每個(gè)主題都可以有任何數(shù)量的觀察者。主題提供一個(gè)接口可以加上或撤銷(xiāo)觀察者對(duì)象;主題角色又叫做抽象被觀察者(Observable)角色;


圖2、抽象主題角色,有時(shí)又叫做抽象被觀察者角色,可以用一個(gè)抽象類(lèi)或者一個(gè)接口實(shí)現(xiàn);在具體的情況下也不排除使用具體類(lèi)實(shí)現(xiàn)。

  . 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個(gè)接口,在得到通知時(shí)更新自己;


圖3、抽象觀察者角色,可以用一個(gè)抽象類(lèi)或者一個(gè)接口實(shí)現(xiàn);在具體的情況下也不排除使用具體類(lèi)實(shí)現(xiàn)。

  . 具體主題(ConcreteSubject)角色:保存對(duì)具體觀察者對(duì)象有用的內(nèi)部狀態(tài);在這種內(nèi)部狀態(tài)改變時(shí)給其觀察者發(fā)出一個(gè)通知;具體主題角色又叫作具體被觀察者角色;


圖4、具體主題角色,通常用一個(gè)具體子類(lèi)實(shí)現(xiàn)。

  .具體觀察者(ConcreteObserver)角色:保存一個(gè)指向具體主題對(duì)象的引用;和一個(gè)與主題的狀態(tài)相符的狀態(tài)。具體觀察者角色實(shí)現(xiàn)抽象觀察者角色所要求的更新自己的接口,以便使本身的狀態(tài)與主題的狀態(tài)自恰。


圖5、具體觀察者角色,通常用一個(gè)具體子類(lèi)實(shí)現(xiàn)。

  下面給出一個(gè)示意性實(shí)現(xiàn)的Java代碼。首先在這個(gè)示意性的實(shí)現(xiàn)里,用一個(gè)Java接口實(shí)現(xiàn)抽象主題角色,這就是下面的Subject接口:


public interface Subject
{
public void attach(Observer observer);

public void detach(Observer observer);

void notifyObservers();
}
代碼清單1、Subject接口的源代碼。

  這個(gè)抽象主題接口規(guī)定出三個(gè)子類(lèi)必須實(shí)現(xiàn)的操作,即 attach() 用來(lái)增加一個(gè)觀察者對(duì)象;detach() 用來(lái)刪除一個(gè)觀察者對(duì)象;和notifyObservers() 用來(lái)通知各個(gè)觀察者刷新它們自己。抽象主題角色實(shí)際上要求子類(lèi)保持一個(gè)以所有的觀察者對(duì)象為元素的列表。

  具體主題則是實(shí)現(xiàn)了抽象主題Subject接口的一個(gè)具體類(lèi),它給出了以上的三個(gè)操作的具體實(shí)現(xiàn)。從下面的源代碼可以看出,這里給出的Java實(shí)現(xiàn)使用了一個(gè)Java向量來(lái)保存所有的觀察者對(duì)象,而 attach() 和 detach() 操作則是對(duì)此向量的元素增減操作。


import java.util.Vector;
import java.util.Enumeration;

public class ConcreteSubject implements Subject
{
public void attach(Observer observer)
{
observersVector.addElement(observer);
}

public void detach(Observer observer)
{
observersVector.removeElement(observer);
}

public void notifyObservers()
{
Enumeration enumeration = observers();
while (enumeration.hasMoreElements())
{
((Observer)enumeration.nextElement()).update();
}
}

public Enumeration observers()
{
return ((Vector) observersVector.clone()).elements();
}
private Vector observersVector = new java.util.Vector();
}
代碼清單2、ConcreteSubject類(lèi)的源代碼。

  抽象觀察者角色的實(shí)現(xiàn)實(shí)際上是最為簡(jiǎn)單的一個(gè),它是一個(gè)Java接口,只聲明了一個(gè)方法,即update()。這個(gè)方法被子類(lèi)實(shí)現(xiàn)后,一被調(diào)用便刷新自己。

public interface Observer
{
void update();
}
代碼清單3、Observer接口的源代碼。

  具體觀察者角色的實(shí)現(xiàn)其實(shí)只涉及update()方法的實(shí)現(xiàn)。這個(gè)方法怎么實(shí)現(xiàn)與應(yīng)用密切相關(guān),因此本類(lèi)只給出一個(gè)框架。
public class ConcreteObserver implements Observer
{
public void update()
{
// Write your code here
}
}
代碼清單4、ConcreteObserver類(lèi)的源代碼。

  雖然觀察者模式的實(shí)現(xiàn)方法可以有設(shè)計(jì)師自己確定,但是因?yàn)閺腁WT1.1開(kāi)始視窗系統(tǒng)的事件模型采用觀察者模式,因此觀察者模式在Java語(yǔ)言里的地位較為重要。正因?yàn)檫@個(gè)原因,Java語(yǔ)言給出了它自己對(duì)觀察者模式的支持。因此,本文建議讀者在自己的系統(tǒng)中應(yīng)用觀察者模式時(shí),不妨利用Java語(yǔ)言所提供的支持。
Java語(yǔ)言提供的對(duì)觀察者模式的支持

  在Java語(yǔ)言的java.util庫(kù)里面,提供了一個(gè)Observable類(lèi)以及一個(gè)Observer接口,構(gòu)成Java語(yǔ)言對(duì)觀察者模式的支持。

  Observer接口

  這個(gè)接口只定義了一個(gè)方法,update()。當(dāng)被觀察者對(duì)象的狀態(tài)發(fā)生變化時(shí),這個(gè)方法就會(huì)被調(diào)用。這個(gè)方法的實(shí)現(xiàn)應(yīng)當(dāng)調(diào)用每一個(gè)被觀察者對(duì)象的notifyObservers()方法,從而通知所有的觀察對(duì)象。


圖6、java.util提供的Observer接口的類(lèi)圖。


package java.util;

public interface Observer
{
/**
* 當(dāng)被觀察的對(duì)象發(fā)生變化時(shí),這個(gè)方法會(huì)被調(diào)用。
*/
void update(Observable o, Object arg);
}
代碼清單5、java.util.Observer接口的源代碼。

  Observable類(lèi)

  被觀察者類(lèi)都是java.util.Observable類(lèi)的子類(lèi)。java.util.Observable提供公開(kāi)的方法支持觀察者對(duì)象,這些方法中有兩個(gè)對(duì)Observable的子類(lèi)非常重要:一個(gè)是setChanged(),另一個(gè)是notifyObservers()。第一個(gè)方法setChanged()被調(diào)用之后會(huì)設(shè)置一個(gè)內(nèi)部標(biāo)記變量,代表被觀察者對(duì)象的狀態(tài)發(fā)生了變化。第二個(gè)是notifyObservers(),這個(gè)方法被調(diào)用時(shí),會(huì)調(diào)用所有登記過(guò)的觀察者對(duì)象的update()方法,使這些觀察者對(duì)象可以更新自己。

  java.util.Observable類(lèi)還有其它的一些重要的方法。比如,觀察者對(duì)象可以調(diào)用java.util.Observable類(lèi)的addObserver()方法,將對(duì)象一個(gè)一個(gè)加入到一個(gè)列表上。當(dāng)有變化時(shí),這個(gè)列表可以告訴notifyObservers()方法那些觀察者對(duì)象需要通知。由于這個(gè)列表是私有的,因此java.util.Observable的子對(duì)象并不知道觀察者對(duì)象一直在觀察著它們。


圖7、Java語(yǔ)言提供的被觀察者的類(lèi)圖。

  被觀察者類(lèi)Observable的源代碼:


package java.util;
public class Observable
{
private boolean changed = false;
private Vector obs;

/** 用0個(gè)觀察者構(gòu)造一個(gè)被觀察者。**/

public Observable()
{
obs = new Vector();
}

/**
* 將一個(gè)觀察者加到觀察者列表上面。
*/
public synchronized void addObserver(Observer o)
{
if (!obs.contains(o))
{
obs.addElement(o);
}
}

/**
* 將一個(gè)觀察者對(duì)象從觀察者列表上刪除。
*/
public synchronized void deleteObserver(Observer o)
{
obs.removeElement(o);
}

/**
* 相當(dāng)于 notifyObservers(null)
*/
public void notifyObservers()
{
notifyObservers(null);
}

/**
* 如果本對(duì)象有變化(那時(shí)hasChanged 方法會(huì)返回true)
* 調(diào)用本方法通知所有登記在案的觀察者,即調(diào)用它們的update()方法,
* 傳入this和arg作為參量。
*/
public void notifyObservers(Object arg)
{
/**
* 臨時(shí)存放當(dāng)前的觀察者的狀態(tài)。參見(jiàn)備忘錄模式。
*/
Object[] arrLocal;

synchronized (this)
{
if (!changed) return;
arrLocal = obs.toArray();
clearChanged();
}

for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}

/**
* 將觀察者列表清空
*/
public synchronized void deleteObservers()
{
obs.removeAllElements();
}

/**
* 將“已變化”設(shè)為true
*/
protected synchronized void setChanged()
{
changed = true;
}

/**
* 將“已變化”重置為false
*/
protected synchronized void clearChanged()
{
changed = false;
}

/**
* 探測(cè)本對(duì)象是否已變化
*/
public synchronized boolean hasChanged()
{
return changed;
}

/**
* 返還被觀察對(duì)象(即此對(duì)象)的觀察者總數(shù)。
*/
public synchronized int countObservers()
{
return obs.size();
}
}
代碼清單6、java.util.Observer接口的源代碼。

  這個(gè)Observable類(lèi)代表一個(gè)被觀察者對(duì)象。一個(gè)被觀察者對(duì)象可以有數(shù)個(gè)觀察者對(duì)象,一個(gè)觀察者可以是一個(gè)實(shí)現(xiàn)Observer接口的對(duì)象。在被觀察者對(duì)象發(fā)生變化時(shí),它會(huì)調(diào)用Observable的notifyObservers方法,此方法調(diào)用所有的具體觀察者的update()方法,從而使所有的觀察者都被通知更新自己。見(jiàn)下面的類(lèi)圖:


圖8、使用Java語(yǔ)言提供的對(duì)觀察者模式的支持。

  發(fā)通知的次序在這里沒(méi)有指明。Observerable類(lèi)所提供的缺省實(shí)現(xiàn)會(huì)按照Observers對(duì)象被登記的次序通知它們,但是Observerable類(lèi)的子類(lèi)可以改掉這一次序。子類(lèi)并可以在單獨(dú)的線(xiàn)程里通知觀察者對(duì)象;或者在一個(gè)公用的線(xiàn)程里按照次序執(zhí)行。

  當(dāng)一個(gè)可觀察者對(duì)象剛剛創(chuàng)立時(shí),它的觀察者集合是空的。兩個(gè)觀察者對(duì)象在它們的equals()方法返回true時(shí),被認(rèn)為是兩個(gè)相等的對(duì)象。
怎樣使用Java對(duì)觀察者模式的支持

  為了說(shuō)明怎樣使用Java所提供的對(duì)觀察者模式的支持,本節(jié)給出一個(gè)非常簡(jiǎn)單的例子。在這個(gè)例子里,被觀察對(duì)象叫做Watched,也就是被監(jiān)視者;而觀察者對(duì)象叫做Watcher。Watched對(duì)象繼承自java.util.Obsevable類(lèi);而Watcher對(duì)象實(shí)現(xiàn)了java.util.Observer接口。另外有一個(gè)對(duì)象Tester,扮演客戶(hù)端的角色。

  這個(gè)簡(jiǎn)單的系統(tǒng)的結(jié)構(gòu)如下圖所示。


圖9、一個(gè)使用Observer接口和Observable類(lèi)的例子。

  在客戶(hù)端改變Watched對(duì)象的內(nèi)部狀態(tài)時(shí),Watched就會(huì)通知Watcher采取必要的行動(dòng)。


package com.javapatterns.observer.watching;

import java.util.Observer;

public class Tester
{
static private Watched watched;
static private Observer watcher;

public static void main(String[] args)
{
watched = new Watched();

watcher = new Watcher(watched);

watched.changeData("In C, we create bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Visual Basic, we visualize bugs.");
}
}

  代碼清單7、Tester類(lèi)的源代碼。


package com.javapatterns.observer.watching;

import java.util.Observable;

public class Watched extends Observable
{
private String data = "";

public String retrieveData()
{
return data;
}

public void changeData(String data)
{
if ( !this.data.equals( data) )
{
this.data = data;
setChanged();
}

notifyObservers();
}
}

  代碼清單8、Watched類(lèi)的源代碼。


package com.javapatterns.observer.watching;

import java.util.Observable;
import java.util.Observer;

public class Watcher implements Observer
{
public Watcher(Watched w)
{
w.addObserver(this);
}

public void update( Observable ob, Object arg)
{
System.out.println("Data has been changed to: ‘" + ((Watched)ob).retrieveData() + "‘");
}
}

  代碼清單9、Watcher類(lèi)的源代碼。

  可以看出,雖然客戶(hù)端將Watched對(duì)象的內(nèi)部狀態(tài)賦值了四次,但是值的改變只有三次:

watched.changeData("In C, we create bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Java, we inherit bugs.");
watched.changeData("In Visual Basic, we visualize bugs.");

  代碼清單10、被觀察者的內(nèi)部狀態(tài)發(fā)生了改變。

  對(duì)應(yīng)地,Watcher對(duì)象匯報(bào)了三次改變,下面就是運(yùn)行時(shí)間程序打印出的信息:

Data has been changed to: ‘In C, we create bugs.‘

Data has been changed to: ‘In Java, we inherit bugs.‘

Data has been changed to: ‘In Visual Basic, we visualize bugs.‘

  代碼清單11、運(yùn)行的結(jié)果。

  菩薩的守瓶龜

  想當(dāng)年齊天大圣為解救師傅唐僧,前往南海普陀山請(qǐng)菩薩降伏妖怪紅孩兒:“菩薩聽(tīng)說(shuō)...恨了一聲,將手中寶珠凈瓶往海心里撲的一摜...只見(jiàn)那海當(dāng)中,翻波跳浪,鉆出個(gè)瓶來(lái),原來(lái)是一個(gè)怪物馱著出來(lái)...要知此怪名和姓,興風(fēng)作浪惡烏龜。”

  使用面向?qū)ο蟮恼Z(yǔ)言描述,烏龜便是一個(gè)觀察者對(duì)象,它觀察的主題是菩薩。一旦菩薩將凈瓶摜到海里,就象征著菩薩作為主題調(diào)用了notifyObservers()方法。在西游記中,觀察者對(duì)象有兩個(gè),一個(gè)是烏龜,另一個(gè)是悟空。悟空的反應(yīng)在這里暫時(shí)不考慮,而烏龜?shù)姆磻?yīng)便是將瓶子馱回海岸。


圖10、菩薩和菩薩的守瓶烏龜。
 
  菩薩作為被觀察者對(duì)象,繼承自O(shè)bservable類(lèi);而守瓶烏龜作為觀察者,繼承自O(shè)bserver接口;這個(gè)模擬系統(tǒng)的實(shí)現(xiàn)可以采用Java對(duì)觀察者模式的支持達(dá)成。
Java中的DEM事件機(jī)制

  AWT中的DEM機(jī)制

  責(zé)任鏈模式一章中曾談到,AWT1.0的事件處理的模型是基于責(zé)任鏈的。這種模型不適用于復(fù)雜的系統(tǒng),因此在AWT1.1版本及以后的各個(gè)版本中,事件處理模型均為基于觀察者模式的委派事件模型(Delegation Event Model或DEM)。

  在DEM模型里面,主題(Subject)角色負(fù)責(zé)發(fā)布(publish)事件,而觀察者角色向特定的主題訂閱(subscribe)它所感興趣的事件。當(dāng)一個(gè)具體主題產(chǎn)生一個(gè)事件時(shí),它就會(huì)通知所有感興趣的訂閱者。

  使用這種發(fā)布-訂閱機(jī)制的基本設(shè)計(jì)目標(biāo),是提供一種將發(fā)布者與訂閱者松散地耦合在一起的聯(lián)系形式,以及一種能夠動(dòng)態(tài)地登記、取消向一個(gè)發(fā)布者的訂閱請(qǐng)求的辦法。顯然,實(shí)現(xiàn)這一構(gòu)思的技巧,是設(shè)計(jì)抽象接口,并把抽象層和具體層分開(kāi)。這在觀察者模式里可以清楚地看到。

  使用DEM的用詞,發(fā)布者叫做事件源(event source),而訂閱者叫做事件聆聽(tīng)者(event listener)。在Java里面,事件由類(lèi)代表,事件的發(fā)布是通過(guò)同步地調(diào)用成員方法做到的。

  Servlet技術(shù)中的的DEM機(jī)制

  AWT中所使用的DEM事件模型實(shí)際上被應(yīng)用到了所有的Java事件機(jī)制上。Servlet技術(shù)中的事件處理機(jī)制同樣也是使用的DEM模型。

  SAX2技術(shù)中的DEM機(jī)制

  DEM事件模型也被應(yīng)用到了SAX2的事件處理機(jī)制上。

  觀察者模式的效果

  觀察者模式的效果有以下的優(yōu)點(diǎn)

  第一、觀察者模式在被觀察者和觀察者之間建立一個(gè)抽象的耦合。被觀察者角色所知道的只是一個(gè)具體觀察者列表,每一個(gè)具體觀察者都符合一個(gè)抽象觀察者的接口。被觀察者并不認(rèn)識(shí)任何一個(gè)具體觀察者,它只知道它們都有一個(gè)共同的接口。

  由于被觀察者和觀察者沒(méi)有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。如果被觀察者和觀察者都被扔到一起,那么這個(gè)對(duì)象必然跨越抽象化和具體化層次。

  第二、觀察者模式支持廣播通訊。被觀察者會(huì)向所有的登記過(guò)的觀察者發(fā)出通知,

  觀察者模式有下面的缺點(diǎn)

  第一、如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話(huà),將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。

  第二、如果在被觀察者之間有循環(huán)依賴(lài)的話(huà),被觀察者會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。在使用觀察者模式是要特別注意這一點(diǎn)。

  第三、如果對(duì)觀察者的通知是通過(guò)另外的線(xiàn)程進(jìn)行異步投遞的話(huà),系統(tǒng)必須保證投遞是以自恰的方式進(jìn)行的。

  第四、雖然觀察者模式可以隨時(shí)使觀察者知道所觀察的對(duì)象發(fā)生了變化,但是觀察者模式?jīng)]有相應(yīng)的機(jī)制使觀察者知道所觀察的對(duì)象是怎么發(fā)生變化的。

  觀察者模式與其它模式的關(guān)系

  觀察者模式使用了備忘錄模式(Memento Pattern)暫時(shí)將觀察者對(duì)象存儲(chǔ)在被觀察者對(duì)象里面。

  問(wèn)答題

  第一題、我和妹妹跟媽媽說(shuō):“媽媽?zhuān)液兔妹迷谠鹤永锿?;飯做好了叫我們一聲?#8221;請(qǐng)問(wèn)這是什么模式?能否給出類(lèi)圖說(shuō)明?

  問(wèn)答題答案

  第一題答案、這是觀察者模式。我和妹妹讓媽媽告訴我們飯做好了,這樣我們就可以來(lái)吃飯了。換用較為技術(shù)化的語(yǔ)言來(lái)說(shuō),當(dāng)系統(tǒng)的主題(飯)發(fā)生變化時(shí),就告訴系統(tǒng)的其它部份(觀察者們,也就是媽媽、我和妹妹),使其可以調(diào)整內(nèi)部狀態(tài)(有開(kāi)始吃飯的準(zhǔn)備),并采取相應(yīng)的行動(dòng)(吃飯)。

  系統(tǒng)的類(lèi)圖說(shuō)明如下。


圖11、系統(tǒng)的類(lèi)圖。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
通俗易懂系列 | 設(shè)計(jì)模式(七):觀察者模式
觀察者模式 Observer 發(fā)布訂閱模式 源 監(jiān)聽(tīng) 行為型 設(shè)計(jì)模式(二十三)
《JAVA與模式》之觀察者模式
設(shè)計(jì)模式之:解剖觀察者模式
15分鐘入門(mén)23種設(shè)計(jì)模式:圖解,范例和對(duì)比
簡(jiǎn)說(shuō)設(shè)計(jì)模式——觀察者模式
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服