目錄
模式簡(jiǎn)介
介紹
優(yōu)點(diǎn)
缺點(diǎn)
代碼實(shí)現(xiàn)
場(chǎng)景說(shuō)明
實(shí)現(xiàn)代碼
運(yùn)行結(jié)果
模式簡(jiǎn)介
觀察者模式(Observer Pattern
),也叫我們熟知的發(fā)布-訂閱模式。
它是一種行為型模式。
介紹
觀察者模式主要關(guān)注的是對(duì)象的一對(duì)多的關(guān)系,
也就是多個(gè)對(duì)象依賴于一個(gè)對(duì)象,當(dāng)該對(duì)象的狀態(tài)發(fā)生改變
時(shí),其他對(duì)象都能夠收到相應(yīng)的通知
意圖:
定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。主要解決:
一個(gè)對(duì)象狀態(tài)改變給其他對(duì)象通知的問(wèn)題,而且要考慮到易用和低耦合,保證高度的協(xié)作。如何解決:
使用面向?qū)ο蠹夹g(shù),可以將這種關(guān)系弱化。
優(yōu)點(diǎn)
- 觀察者和被觀察者是抽象耦合的;
- 建立了一套觸發(fā)機(jī)制。
缺點(diǎn)
- 如果一個(gè)被觀察者對(duì)象有很多的直接和間接的觀察者的話,通知所有的觀察者需要花費(fèi)很長(zhǎng)的時(shí)間;
- 如果在觀察者和被觀察者目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會(huì)觸發(fā)他們之間的循環(huán)調(diào)用,可能會(huì)導(dǎo)致系統(tǒng)崩潰;
- 觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅是知道觀察目標(biāo)發(fā)生了變化。
代碼實(shí)現(xiàn)
場(chǎng)景說(shuō)明
我們現(xiàn)在有三個(gè)觀察者和三種消息,這三個(gè)觀察者分別對(duì)不同的消息感興趣
我們實(shí)現(xiàn)的話,可以簡(jiǎn)單的設(shè)置三個(gè)觀察者,一個(gè)主題類(lèi)(被觀察者
)。
這三個(gè)觀察者可以設(shè)置自己喜歡的、感興趣的消息類(lèi)型(1、2、3
)。
他們處理收到的消息就是打印一下自己收到了什么消息。
具體實(shí)現(xiàn)如下
實(shí)現(xiàn)代碼
/*
observer1 observer2 observer3
Subject (主題)主題更改,應(yīng)該及時(shí)通知相應(yīng)的觀察者,去處理相應(yīng)的事件
*/
class Observer // 觀察者抽象類(lèi)
{
public:
//處理消息的接口
virtual void handle(int msgid) = 0;
};
//第一個(gè)觀察者實(shí)例
class Observer1 : public Observer
{
public:
void handle(int msgid)
{
switch (msgid)
{
case 1:
cout << "Observer1 recv 1 msg" << endl;
break;
case 2:
cout << "Observer1 recv 2 msg" << endl;
break;
default:
cout << "Observer1 recv unkonw msg!" << endl;
break;
}
}
};
//第二個(gè)觀察者實(shí)例
class Observer2 : public Observer
{
public:
void handle(int msgid)
{
switch (msgid)
{
case 2:
cout << "Observer2 recv 2 msg" << endl;
break;
default:
cout << "Observer2 recv unkonw msg!" << endl;
break;
}
}
};
//第三個(gè)觀察者實(shí)例
class Observer3 : public Observer
{
public:
void handle(int msgid)
{
switch (msgid)
{
case 1:
cout << "Observer3 recv 1 msg" << endl;
break;
case 3:
cout << "Observer3 recv 3 msg" << endl;
break;
default:
cout << "Observer3 recv unkonw msg!" << endl;
break;
}
}
};
//主題類(lèi)
class Subject
{
public:
//給主題增加觀察者對(duì)象
void addObserver(Observer* obser,int msgid)
{
_subMap[msgid].push_back(obser);
}
//主題檢測(cè)發(fā)生改變,通知相應(yīng)的觀察者對(duì)象處理事件
void dispatch(int msgid)
{
auto it = _subMap.find(msgid);
if (it != _subMap.end())
{
//通過(guò)多態(tài),實(shí)現(xiàn)不同的指向
for (Observer* pObser : it->second)
{
pObser->handle(msgid);
}
}
}
private:
//用來(lái)保存訂閱的消息
unordered_map<int, list<Observer*>> _subMap;
};
我們可以看到主題類(lèi)(Subject
)的數(shù)據(jù)成員是一個(gè)unordered_map。使用這個(gè)是因?yàn)槲覀儾恍枰獢?shù)據(jù)是有序的,為了提高增刪查的速率
,使用了無(wú)序
map。
使用map的好處是,它作為一個(gè)鍵值對(duì)
,可以存儲(chǔ)我們想要的數(shù)據(jù)類(lèi)型:(消息類(lèi)型,訂閱此消息類(lèi)型的觀察者們)。
并且,在主題類(lèi)(Subject
)的成員方法addObserver
中,我們使用了一個(gè)中括號(hào)運(yùn)算符([]
)重載的特性:
如果當(dāng)前容器中存有相應(yīng)的msgid
鍵的話,就直接添加對(duì)應(yīng)的值(Obser
);
如果當(dāng)前容器中沒(méi)有相應(yīng)的msgid
鍵的話,就直接添加該鍵,并且添加一個(gè)默認(rèn)的值。
運(yùn)行結(jié)果
我們使用如下的代碼:
void main()
{
Subject subject;
Observer* p1 = new Observer1();
Observer* p2 = new Observer2();
Observer* p3 = new Observer3();
subject.addObserver(p1, 1);
subject.addObserver(p1, 2);
subject.addObserver(p2, 2);
subject.addObserver(p3, 1);
subject.addObserver(p3, 3);
int msgid = 0;
for (;;)
{
cout << "請(qǐng)輸入消息id:" << endl;
cin >> msgid;
if (msgid == -1)
break;
subject.dispatch(msgid);//發(fā)起通知
}
}
運(yùn)行結(jié)果如下文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-618691.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-618691.html
到了這里,關(guān)于C++之觀察者模式(發(fā)布-訂閱)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!