設計模式------觀察者模式(Oberserver Pattern)
定義:觀察者模式-在對象之間定義一對多的依賴。這樣以來,一個對象改變狀態時,依賴它的對象都會收到通知,並自動更新。
OO設計的的思想裡,我們強調,我們的設計總是在為對象之間的互動的松耦合而努力。觀察者模式,正是在一對多的依賴中,尋找一種松耦合的實現方式。我們在日常生活中,經常會遇到這種情況,就是某個對象擁有某些狀態,而其它對象都需要使用這些狀態,從而執行某些行為。比如,在一個資料控制中心,有一個裝置來負責採集資料,並把資料即時的告知其它的部分,這些部門需要利用這些資料。因此,這就是一個一對多的依賴關係。假設我們以衛星控制系統來描述這個模式。
假設系統的行為為:有一個資料擷取中心,負責擷取衛星的即時資料:經度,緯度,飛行時常。
資料擷取中西負責把及時採集到的資料通知其它部門,假設有兩個部門,一個負責即時顯示衛星的目前狀態,另外一個部門,負責通過接收到的資料進行計算,並輸出下一步的控制訊號。並且,該系統還不夠完善,可能會增加新的部門,該部門也需要利用衛星的即時資料,做其它的工作。
根據以上的系統需求,我們可以知道,這是一個一對多的關係,並且,需要系統有很好的擴充性,即系統對象之間的耦合性要小。那我們怎麼來實現呢?
系統有一個資料擷取中心,我們需要來設計一個類來描述這個資料擷取中心,假設叫做SatelliteData,
那麼顯示部門也用一個類DisplayBoard類以及計算部門也用一個類Calculate來描述。類SatelliteData定義如下所示:
class SatelliteData
{
float longitude;
float latitude;
float timeLast;
public float getLongitude()
{
……
}
public float getLatitude()
{
……
}
public float getTimeLast()
{
……
}
public setChange()
{
longitude=getLongitude();
latitude=getLatitude();
timeLast=getTimeLast();
displayBoard.update(longitudu,latitude,timeLast);
calculate.update(longitudu,latitude,timeLast);
}
}
其中displayBoard是類DisplayBoard的對象,calculate是Calculate的對象。我們來看看這段代碼,實際上違背了我們在策略模式中一再強調的面向介面編程的思想,紅色部分的代碼是面向實現的,針對一個個的具體需要用到這些狀態的對象,發送通知。這樣,如果再增加新的使用對象,那就需要修改SatelliteDate類的代碼,並且,這樣造成的耦合性很大。
那我們用觀察者模式如何來解決這個問題呢?觀察者模式定義了一對多的依賴,其中“一”被稱作主題,而“多”被稱作觀察者。觀察者通過註冊的方式來訂閱主題,當不需要的時候,再通過主題取消訂閱。這樣,多個觀察者通過訂閱的方式共用主題裡的內容。當主題內容改變的時候,需要動態通知觀察者,以便他們即使做出響應。
在觀察者模式中,主題實現了Subject 介面,Subject介面主要定義了三個方法,即註冊觀察者,通知觀察者目前狀態以及刪除某個觀察者。
定義如下:
interface Subject
{
public void registerOberserver(Oberserver o);
public void notifyOberservers(Oberserver o);
public void deleteOberserver(Oberserver o);
}
在各個方法的參數中,Oberserver是觀察者介面,具體的觀察者實現該介面,這樣,在這裡,我們體現了面向介面編程的思想。
觀察者對象需要實現Oberserber介面,這個介面主要定義了更新的方法,update();這個方法主要是為了主題在更新狀態是調用,這樣在面向介面編程中,主題只關心介面實現的方法,具體由哪個類實現的,主題並不關心。我們來看相關的類圖.
由類圖可以看倒,SatelliteData類實現了Subject介面,而DisplayBoard類和Calculate類也分別實現了Oberserver介面,並且SatelliteData擁有多個註冊的觀察者,觀察者共用同一個主題。代碼如下:
//主題介面定義
interface Subject
{
public void registerOberserver(Oberserver o);
public void notifyOberservers(Oberserver o);
public void deleteOberserver(Oberserver o);
}
//觀察者介面定義
interface Oberserver
{
public void update(float longtitude,float latitude,float lastTime);
}
//類衛星資料定義
class SatelliteData
{
float longitude;
float latitude;
float timeLast;
ArrayList oberservers;//註冊的觀察者列表
public SatelliteDate()
{
oberservers=new ArralyList();
}
public float getLongitude()
{
……
}
public float getLatitude()
{
……
}
public float getTimeLast()
{
……
}
public setChange()
{
longitude=getLongitude();
latitude=getLatitude();
timeLast=getTimeLast();
notifyOberservers();
}
public void registerOberserver(Oberserver o)
{
oberservers.add(o);
}
public void deleteOberserver(Oberserver o)
{
int i=Oberservers.indexOf(o);
oberservers.remove(i);
}
public void notifyOberservers()
{
longtitude=getLongtitude();
latitude=getLatitude();
timeLast=getTimeLast();
for(int i=0;i<oberservers.size();i++)
{
Oberserver oberserver=(Oberserver)oberservers.get(i);
oberserber.update(longitude,latitude,timeLast);
}
}
}
//顯示部門類
class DisplayBoard implements Oberserver
{
SatelliteDate data;
public DisplayData(SatelliteDate data)
{
this.data=data;
}
public void update(float longtitude,float latitude,float timeLast)
{
Syste.out.println("經度"+longtitude +"緯度:"+latitude+"持續時間長度"+timeLast);
}
//計算類
class Calculate implements Oberserver
{
SatelliteDate data;
public DisplayData(SatelliteDate data)
{
this.data=data;
}
public void update(float longtitude,float latitude,float timeLast)
{
//這是計算過程
}
}
觀察者模式總結:
1,在物件導向設計過程中,依賴關係必不可少,我們經常在為對象間的松耦合設計而努力,這是我們進行OO設計的又一個準則,除了面向介面編程以外,還需要的是把是一個變為有一個(is a to has a)。
2,觀察者模式的適用範圍,就如定義所言,適用於一對多的依賴關係,一個對象狀態的改變影響所有其它對象,比如在銀行系統中,客戶帳戶的利息的計算是依賴於銀行的利率的,那銀行的利率的改變,必須由銀行象所有的客戶通知,從而利息的計算結果也會發生變化。這裡,銀行相當於一個主題,銀行客戶就是觀察者來訂閱這個主題。可以在這裡應用觀察者模式。