#觀察者模式(Observer Pattern)
##簡介
***觀察者模式(有時又被稱為發布/訂閱模式)是軟件設計模式的一種。在此種模式中,一個目標對象管理所有相依于它的觀察者對象,并且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。***

###抽象目標類別
此抽象類別提供一個界面讓觀察者進行添附與解附作業。此類別內有個不公開的觀察者串煉,并透過下列函式(方法)進行作業
* 添附(Attach):新增觀察者到串煉內,以追蹤目標對象的變化。
* 解附(Detach):將已經存在的觀察者從串煉中移除。
* 通知(Notify):利用觀察者所提供的更新函式來通知此目標已經產生變化。
添附函式包涵了一個觀察者對象參數。也許是觀察者類別的虛擬函式(即更新函式),或是在非面向對象的設定中所使用的函式指標(更廣泛來講,函式子或是函式對象)。
###目標類別
此類別提供了觀察者欲追蹤的狀態。也利用其源類別(例如前述的抽象目標類別)所提供的方法,來通知所有的觀察者其狀態已經更新。此類別擁有以下函式
* 取得狀態(GetState):回傳該目標對象的狀態。
###抽象觀察者界面
抽象觀察者類別是一個必須被實做的抽象類別。這個類別定義了所有觀察者都擁有的更新用界面,此界面是用來接收目標類別所發出的更新通知。此類別含有以下函式
* 更新(Update):會被實做的一個抽象(虛擬)函式。
###觀察者類別
這個類別含有指向目標類別的參考(reference),以接收來自目標類別的更新狀態。此類別含有以下函式
* 更新(Update):是前述抽象函式的實做。當這個函式被目標對象呼叫時,觀察者對象將會呼叫目標對象的取得狀態函式,來其所擁有的更新目標對象資訊。
每個觀察者類別都要實做它自己的更新函式,以應對狀態更新的情形。
當目標對象改變時,會通過呼叫它自己的通知函式來將通知送給每一個觀察者對象,這個通知函式則會去呼叫已經添附在串煉內的觀察者更新函式。通知與更新函式可能會有一些參數,好指明是目前目標對象內的何種改變。這么作將可增進觀察者的效率(只更新那些改變部分的狀態)。
###用途
* 當抽象個體有兩個互相依賴的層面時。封裝這些層面在單獨的對象內將可允許程序員單獨地去變更與重復使用這些對象,而不會產生兩者之間交互的問題。
* 當其中一個對象的變更會影響其他對象,卻又不知道多少對象必須被同時變更時。
* 當對象應該有能力通知其他對象,又不應該知道其他對象的實做細節時。
```
=== C++ ===
<source lang="cpp">
#include <list>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
// The Abstract Observer
class ObserverBoardInterface
{
public:
virtual void update(float a,float b,float c) = 0;
};
// Abstract Interface for Displays
class DisplayBoardInterface
{
public:
virtual void show() = 0;
};
// The Abstract Subject
class WeatherDataInterface
{
public:
virtual void registerob(ObserverBoardInterface* ob) = 0;
virtual void removeob(ObserverBoardInterface* ob) = 0;
virtual void notifyOb() = 0;
};
// The Concrete Subject
class ParaWeatherData: public WeatherDataInterface
{
public:
void SensorDataChange(float a,float b,float c)
{
m_humidity = a;
m_temperature = b;
m_pressure = c;
notifyOb();
}
void registerob(ObserverBoardInterface* ob)
{
m_obs.push_back(ob);
}
void removeob(ObserverBoardInterface* ob)
{
m_obs.remove(ob);
}
protected:
void notifyOb()
{
list<ObserverBoardInterface*>::iterator pos = m_obs.begin();
while (pos != m_obs.end())
{
((ObserverBoardInterface* )(*pos))->update(m_humidity,m_temperature,m_pressure);
(dynamic_cast<DisplayBoardInterface*>(*pos))->show();
++pos;
}
}
private:
float m_humidity;
float m_temperature;
float m_pressure;
list<ObserverBoardInterface* > m_obs;
};
// A Concrete Observer
class CurrentConditionBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
CurrentConditionBoard(WeatherDataInterface& a):m_data(a)
{
m_data.registerob(this);
}
void show()
{
cout<<"_____CurrentConditionBoard_____"<<endl;
cout<<"humidity: "<<m_h<<endl;
cout<<"temperature: "<<m_t<<endl;
cout<<"pressure: "<<m_p<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
m_h = h;
m_t = t;
m_p = p;
}
private:
float m_h;
float m_t;
float m_p;
WeatherDataInterface& m_data;
};
// A Concrete Observer
class StatisticBoard : public ObserverBoardInterface, public DisplayBoardInterface
{
public:
StatisticBoard(WeatherDataInterface& a):m_maxt(-1000),m_mint(1000),m_avet(0),m_count(0),m_data(a)
{
m_data.registerob(this);
}
void show()
{
cout<<"________StatisticBoard_________"<<endl;
cout<<"lowest temperature: "<<m_mint<<endl;
cout<<"highest temperature: "<<m_maxt<<endl;
cout<<"average temperature: "<<m_avet<<endl;
cout<<"_______________________________"<<endl;
}
void update(float h, float t, float p)
{
++m_count;
if (t>m_maxt)
{
m_maxt = t;
}
if (t<m_mint)
{
m_mint = t;
}
m_avet = (m_avet * (m_count-1) + t)/m_count;
}
private:
float m_maxt;
float m_mint;
float m_avet;
int m_count;
WeatherDataInterface& m_data;
};
int main(int argc, char *argv[])
{
ParaWeatherData * wdata = new ParaWeatherData;
CurrentConditionBoard* currentB = new CurrentConditionBoard(*wdata);
StatisticBoard* statisticB = new StatisticBoard(*wdata);
wdata->SensorDataChange(10.2, 28.2, 1001);
wdata->SensorDataChange(12, 30.12, 1003);
wdata->SensorDataChange(10.2, 26, 806);
wdata->SensorDataChange(10.3, 35.9, 900);
wdata->removeob(currentB);
wdata->SensorDataChange(100, 40, 1900);
delete statisticB;
delete currentB;
delete wdata;
return 0;
}
```
將一個系統分割成一系列相互作用的類有個很不好的副作用,那就是需要維護相關對象間的一致性。我們不希望為了維持一致性而逝各類緊密耦合,這樣會給維護,擴展和重用都帶來不便。
什么時候考慮使用觀察者模式呢。
***當一個對象的改變需要同事改變其它對象而且它不知道具體有多少對象有待改變時應該考慮使用觀察者模式。***
一個抽象模型有兩個方面,其中一方面依賴于另一方面,這時用觀察者模式可以將這兩者防撞在獨立的對象中使它們各自獨立地改變和服用。
***總的來講,觀察者模式所做的工作其實就是在解除耦合,讓耦合的雙方都依賴于抽象,而不是依賴于具體。從而使得各自的變化都不會影響另一個的變化。***