觀察者模式(Observer)定義了對象間一對多的聯系。當一個對象的狀態發生變化時,所有與它有聯系的觀察者對象都會得到通知。觀察者模式將被觀察的目標和觀察者解耦,一個目標可以有任意多的觀察者,觀察者也可以觀察任意多的目標,構成復雜的聯系,而每個觀察者都不知道其他觀察者的存在。
這裡我就不列出那麼多正式化的UML圖了,那些是書本上的事,如果要詳細學設計模式,推薦Gof的大話設計模式
我們在這裡舉兩個例子來說明
第一:燒水瓶熱水時候,當燒到97度的時候,顯示屏會收到消息,然後再屏幕上顯示當前溫度,聲控器會發出聲音,提示報警!
第二:某軟件公司突然來了一個性感的女秘書,程序員A和程序員B這兩個釣絲按耐不住了,密切注視著這位女秘書,秘書的一舉一動都會影響到他們!
我們先來看第一個問題,仔細分析一下大多數人都會寫出如下代碼:
[cpp]
class Heater
{
private:
signed int temperature;
public:
//熱水機燒水
void BoilWater()
{
for(int i=0;i<=100;++i)
{
temperature = i;
if(temperature == 97)
{
ShowMSG();
MakeAlert();
}
}
}
//顯示屏顯示
void ShowMSG()
{
cout<<"誰快開了,當前溫度:"<<temperature<<",別再燒了"<<endl;
}
//警報器發出警報
void MakeAlert()
{
cout<<"Alarm:嘀嘀嘀,水已經"<<temperature<<"度了"<<endl;
}
};
class Heater
{
private:
signed int temperature;
public:
//熱水機燒水
void BoilWater()
{
for(int i=0;i<=100;++i)
{
temperature = i;
if(temperature == 97)
{
ShowMSG();
MakeAlert();
}
}
}
//顯示屏顯示
void ShowMSG()
{
cout<<"誰快開了,當前溫度:"<<temperature<<",別再燒了"<<endl;
}
//警報器發出警報
void MakeAlert()
{
cout<<"Alarm:嘀嘀嘀,水已經"<<temperature<<"度了"<<endl;
}
};
這樣的設計雖然可以達到目的,但是方式不太好,我們知道也許燒水器,顯示屏和警報器不一定是同一個廠家生產的,所以我們應該把他們分割成幾個類,代碼如下:
[cpp]
//燒水器(被觀察者)
class Heater
{
private:
signed int temperature;
public:
//熱水機燒水
void BoilWater()
{
for(int i=0;i<=100;++i)
{
temperature = i;
if(temperature == 97)
{
//向監聽他的兩個類發出信息
}
}
}
};
//顯示屏類(觀察著)
class show
{
public:
void ShowMSG(int param)
{
cout<<"誰快開了,當前溫度:"<<param<<",別再燒了"<<endl;
}
};
//警報器類(觀察著)
class alarm
{
public:
void MakeAlert(int param)
{
cout<<"Alarm:嘀嘀嘀,水已經"<<temperature<<"度了"<<endl;
}
}
//燒水器(被觀察者)
class Heater
{
private:
signed int temperature;
public:
//熱水機燒水
void BoilWater()
{
for(int i=0;i<=100;++i)
{
temperature = i;
if(temperature == 97)
{
//向監聽他的兩個類發出信息
}
}
}
};
//顯示屏類(觀察著)
class show
{
public:
void ShowMSG(int param)
{
cout<<"誰快開了,當前溫度:"<<param<<",別再燒了"<<endl;
}
};
//警報器類(觀察著)
class alarm
{
public:
void MakeAlert(int param)
{
cout<<"Alarm:嘀嘀嘀,水已經"<<temperature<<"度了"<<endl;
}
}
如果像上面的設計,顯示屏類和警報器類都在監督著燒水器的溫度,當達到97度的時候,燒水器就會像他的兩個觀察著發出信息,一邊執行必要的操作,可是在C++不像C#有委托和事件處理,所以在C++中需要用到多態的方式,利用vector來操作消息處理,相信這個例子已經很形象的講述了觀察者模式了,接下來讓我們具體實現第二個例子
例子結構如下:
IObservable,被觀察者接口
CmishuObservable,被觀察者秘書
IObserver,觀察者接口
CCoderBObserver,觀察者程序員B
CCoderAObserver觀察者程序員A
[cpp] view plaincopyprint?
class IObservable //被觀察者接口
{
IObservable() {}
virtual ~IObservable() {}
virtual void AddObserver(IObserver *pObserver) = 0; //所有被觀察者都有的操作
virtual void DeleteObserver(IObserver *pObserver) = 0;
virtual void NotifyObservers(string context) = 0;
};
class CmishuObservable:public IObservable //秘書繼承並實現了接口的函數
{
private:
vector<IObserver *> m_ObserverList; //用於盛裝觀察者的容器
typedef vector<IObserver *>::iterator Observer_iter;
public:
virtual void AddObserver(IObserver *pObserver)
{
m_ObserverList.push_back(pObserver); //把觀察者加入到容器中
}
virtual void DeleteObserver(IObserver *pObserver) //刪除觀察著
{
Observer_iter it = m_ObserverList.begin();
for(;it!= m_ObserverList.end();++it)
{
if(pObserver == *it) //這裡有點處理得不好,不該比指針,權當例子吧
m_ObserverList.erase(it);
}
}
virtual void NotifyObservers(string context) //通告觀察者,以便執行更新
{
Observer_iter it = m_ObserverList.begin();
for(;it != m_ObserverList.end();++it)
(*it)->update(context);
}
void HaveBreakfast() //秘書的行為
{
cout<<"美女秘書:開始吃飯去了,餓了啦"<<endl;
this->NotifyObservers(string("開始吃飯去了,餓了啦"));
}
void HaveFun() //秘書的行為
{
cout<<"美女秘書:人家好想去玩啊!"<<endl;
this->NotifyObservers(string("人家好想去玩啊!"));
}
};
class IObserver //觀察者接口
{
public:
IObserver() {}
virtual ~IObserver() {};
virtual void update(string context) = 0;
};
class CCoderAObserver:public IObserver //程序員A繼承並實現update
{
private:
string name;
public:
CCoderAObserver()
{
name = "Coder A";
}
virtual void update(string context)
{
//收到消息:我好想去玩,或者去吃飯
//執行相應的操作
}
};
class CCoderBObserver:public IObserver //程序員B並實現update
{
private:
string name;
public:
CCoderBObserver()
{
name = "Coder B";
}
virtual void update(string context)
{
//收到消息:我好想去玩,或者去吃飯
//執行相應的操作
}
};
class IObservable //被觀察者接口
{
IObservable() {}
virtual ~IObservable() {}
virtual void AddObserver(IObserver *pObserver) = 0; //所有被觀察者都有的操作
virtual void DeleteObserver(IObserver *pObserver) = 0;
virtual void NotifyObservers(string context) = 0;
};
class CmishuObservable:public IObservable //秘書繼承並實現了接口的函數
{
private:
vector<IObserver *> m_ObserverList; //用於盛裝觀察者的容器
typedef vector<IObserver *>::iterator Observer_iter;
public:
virtual void AddObserver(IObserver *pObserver)
{
m_ObserverList.push_back(pObserver); //把觀察者加入到容器中
}
virtual void DeleteObserver(IObserver *pObserver) //刪除觀察著
{
Observer_iter it = m_ObserverList.begin();
for(;it!= m_ObserverList.end();++it)
{
if(pObserver == *it) //這裡有點處理得不好,不該比指針,權當例子吧
m_ObserverList.erase(it);
}
}
virtual void NotifyObservers(string context) //通告觀察者,以便執行更新
{
Observer_iter it = m_ObserverList.begin();
for(;it != m_ObserverList.end();++it)
(*it)->update(context);
}
void HaveBreakfast() //秘書的行為
{
cout<<"美女秘書:開始吃飯去了,餓了啦"<<endl;
this->NotifyObservers(string("開始吃飯去了,餓了啦"));
}
void HaveFun() //秘書的行為
{
cout<<"美女秘書:人家好想去玩啊!"<<endl;
this->NotifyObservers(string("人家好想去玩啊!"));
}
};
class IObserver //觀察者接口
{
public:
IObserver() {}
virtual ~IObserver() {};
virtual void update(string context) = 0;
};
class CCoderAObserver:public IObserver //程序員A繼承並實現update
{
private:
string name;
public:
CCoderAObserver()
{
name = "Coder A";
}
virtual void update(string context)
{
//收到消息:我好想去玩,或者去吃飯
//執行相應的操作
}
};
class CCoderBObserver:public IObserver //程序員B並實現update
{
private:
string name;
public:
CCoderBObserver()
{
name = "Coder B";
}
virtual void update(string context)
{
//收到消息:我好想去玩,或者去吃飯
//執行相應的操作
}
};