原理或定義
定義了對象間的一種一對多依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。
結構
觀察者Observer:所有潛在的觀察者必須實現觀察者接口,這個接口只有update方法,當主題改變時,它被調用。
具體觀察者ConcreteObserver: 具體觀察者可以是任何實現了Observer接口的類。觀察者必須註冊具體主題,一邊接收更新。
可觀察者Subject: 主題接口,即可觀察者Observable,對象使用此接口註冊爲觀察者,或者把自己從觀察者中刪除,每個主題可以有多個觀察者。
具體可觀察者ConcreteSubject: 一個具體主題實現了主題接口,除了註冊和撤銷之外,具體主題還實現了notifyObservers()方法,這個方法用來在主題狀態改變時更新所有觀察者。具體主題也可能有設置和獲取狀態的方法。
類圖
案例和代碼
本模式以天氣站項目作爲示例Internet氣象站項目:
提供溫度、氣壓和溼度的接口
測量數據更新時需時時通知給第三方
需要設計開放型API,便於其他第三方公司也能接入氣象站獲取數據
WeatherData類:
一個通常的設計方案:
有些問題
1)其他第三方公司接入氣象站獲取數據的問題
2)無法在運行時動態的添加第三方
用觀察者模式設計重新設計的方案:
觀察者Observer
public interface Observer {
public void update(float mTemperatrue,float mPressure,float mHumidity);
}
可觀察者Subject
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
具體觀察者ConcreteObserver
public class CurrentConditions implements Observer {
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(float mTemperatrue, float mPressure, float mHumidity) {
// TODO Auto-generated method stub
this.mHumidity = mHumidity;
this.mPressure = mPressure;
this.mTemperatrue = mTemperatrue;
display();
}
public void display() {
System.out.println("***Today mTemperatrue:" + mTemperatrue + "***");
System.out.println("***Today mPressure:" + mPressure + "***");
System.out.println("***Today mHumidity:" + mHumidity + "***");
}
}
public class ForcastConditions implements Observer{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
@Override
public void update(float mTemperatrue, float mPressure, float mHumidity) {
// TODO Auto-generated method stub
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
display();
}
public void display()
{
System.out.println("**明天溫度:"+(mTemperatrue+Math.random())+"**");
System.out.println("**明天氣壓:"+(mPressure+10*Math.random())+"**");
System.out.println("**明天溼度:"+(mHumidity+Math.random())+"**");
}
}
具體可觀察者ConcreteSubject
public class WeatherDataSt implements Subject{
private float mTemperatrue;
private float mPressure;
private float mHumidity;
private ArrayList<Observer> mObservers;
public WeatherDataSt()
{
mObservers=new ArrayList<Observer>();
}
public float getTemperature()
{
return mTemperatrue;
}
public float getPressure()
{
return mPressure;
}
public float getHumidity()
{
return mHumidity;
}
public void dataChange()
{
notifyObservers();
}
public void setData(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}
@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
mObservers.add(o);
}
@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(mObservers.contains(o))
{mObservers.remove(o);}
}
@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i=0,len=mObservers.size();i<len;i++)
{
mObservers.get(i).update(getTemperature(), getPressure(), getHumidity());
}
}
}
管理者 / 測試方法
public class InternetWeather {
public static void main(String[] args) {
CurrentConditions mCurrentConditions;
ForcastConditions mForcastConditions;
WeatherDataSt mWeatherDataSt;
mWeatherDataSt=new WeatherDataSt();
mCurrentConditions=new CurrentConditions();
mForcastConditions=new ForcastConditions();
mWeatherDataSt.registerObserver(mCurrentConditions);
mWeatherDataSt.registerObserver(mForcastConditions);
mWeatherDataSt.setData(30, 150, 40);
mWeatherDataSt.removeObserver(mCurrentConditions);
mWeatherDataSt.setData(40, 250, 50);
}
}
使用場景
1、 對一個對象狀態的更新,需要其他對象同步更新,而且其他對象的數量動態可變。
2、 對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細節。
優缺點
主要優點有:
1、 Subject和Observer之間是鬆偶合的,分別可以各自獨立改變。
2、 Subject在發送廣播通知的時候,無須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。
3、 遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。
缺點主要有:
1、 鬆偶合導致代碼關係不明顯,有時可能難以理解。(廢話)
2、 如果一個Subject被大量Observer訂閱的話,在廣播通知的時候可能會有效率問題。