觀察者模式
當對象存在一對多的關係時,則使用觀察者模式(Observer Pattern)。比如,當一個對象被修改時,則會自動通知它依賴對象。觀察者模式屬於行爲型模式,又被稱爲發佈-訂閱模式、模式-視圖模式、源-監聽器模式或從屬者模式。觀察者模式中分爲目標和觀察者,目標可以被多個觀察者所觀察,目標的狀態變化發生變化時,觀察該目標的所有觀察者將得到通知,通常被用作事件的實時處理。這種模式類似於電影上搖骰子的情況,這時那些賭徒就是觀察者,那個骰子就是目標,當骰盅被打開時會有一個人來讀點,用來通知給那些下注的賭徒,那些賭徒會根據骰子的點數作出相應的動作。
適用情景:
(1)C/S模式下的消息實時發佈。
(2)股票大盤的消息實時更新。
模式結構
觀察者模式中具體分爲:
(1)目標(subject):目標將所有觀察者對象保存在一個聚集(數組、鏈表)裏,每個目標都可以擁有任意數量的觀察者。目標提供一個接口,可以增加和刪除觀察者的對象,目標又被稱爲:抽象主題角色或被觀察者角色,一般使用一個抽象類或接口實現。
(2)具體目標 (Concrete Subject):將有關狀態狀態存入具體觀察者對象;當具體目標的內部狀態改變時,給登記過的觀察者(觀察該目標的所有觀察者)發出通知。具體目標又被稱爲:具體主題角色或具體被觀察者角色,一般使用一個具體的子類實現。
(3) 觀察者 (observer):爲所有的具體觀察者定義一個接口,在得到目標的通知時更新自己。這個接口叫做更新接口。觀察者一般用一個抽象類或者一個接口實現。在抽像類中,更新接口只包含一個方法(updata方法),這個方法叫做更新方法。觀察者又被稱爲抽象觀察者角色。
(4)具體觀察者(Concrete Observer):存儲與目標的狀態自恰的狀態。具體觀察者實現了觀察者的所有更新接口,以便使本身的狀態與目標狀態協調。如果需要,具體觀察者角色可以保存一個指向具體目標對象的引用或指針。具體觀察者又被稱爲具體觀察者角色,通常使用一個具體的子類實現。
其中,目標和觀察者屬於抽像類爲具體目標和具體觀察者提供接口。
觀察者模式的優點:
(1)觀察者模式在目標和觀察者之間建立了一個抽象的耦合。被觀察者角色所知道的只是一個具體的觀察者的聚集,每一個具體的觀察者都符合一個抽象觀察者的接口。目標並不認識任何一個具體的觀察者,它只知道它們都有一個共同的接口。由於目標和觀察者沒有緊密的耦合在一起,因此它們可以屬於不同的抽象化層次。
(2)觀察者模式支持廣播通信。目標會向所有的登記過的觀察者發出通知。
觀察者模式的缺點:
(1)如果一個目標有很多直接和間接的觀察者時,將所有的觀察者都通知到會花費很多時間。
(2)如果在目標和觀察者之間有循環依賴的化,目標會觸發它們的循環調用,導致程序崩潰。
(3)觀察無法知道目標時如何變化的,只能被動的接受變化的結果。
具體實現
#include <iostream>
#include <string>
#include <list>
using namespace std;
//觀察者
class Observer
{
public:
Observer()
{
}
virtual ~Observer()
{
}
//負責更新目標的通知
virtual void updata() = 0;
};
//目標
class Subject
{
public:
Subject()
{
}
virtual ~Subject()
{
}
void addObserver(Observer* pObject); //添加觀察者
void delObserver(Observer* pObject); //刪除觀察者
void noticeAllObserver(); //通知觀察者
virtual int getStatus() = 0; //獲取目標狀態
virtual void setStatus(int) = 0; //設置目標狀態
private:
list<Observer*> observerList; //用來保存觀察者的隊列
};
void Subject::addObserver(Observer* pObject)
{
observerList.push_back(pObject);
}
void Subject::delObserver(Observer* pObject)
{
list<Observer*>::iterator iter = observerList.begin();
for (; iter != observerList.end(); ++iter)
{
if (*iter == pObject)
{
iter = observerList.erase(iter);
break;
}
}
}
void Subject::noticeAllObserver()
{
list<Observer*>::iterator iter = observerList.begin();
for (; iter != observerList.end(); ++iter)
{
(*iter)->updata();
}
}
//實際觀察者
class concreteObserver :public Observer
{
public:
concreteObserver(string obName, Subject* subject)
:observerName(obName), mySubject(subject)
{
}
~concreteObserver()
{
}
void updata()
{
cout << "updata:[myName:" << observerName << ",mySubjectStatus:" << mySubject->getStatus() << "]" << endl;
}
private:
string observerName; //觀察者名稱
Subject* mySubject; //觀察者觀察的目標
};
//實際目標
class concreteSubject:public Subject
{
public:
concreteSubject(string subName,int status)
:subjectName(subName), subjectStatus(status)
{
cout << "subjectName:" << subjectName << ",subjectStatus:" << subjectStatus << endl;
}
int getStatus()
{
return subjectStatus;
}
void setStatus(int status)
{
subjectStatus = status;
cout << "subjectName:" << subjectName << ",subjectStatus:" << subjectStatus << endl;
}
private:
string subjectName; //目標名稱
int subjectStatus; //目標狀態
};
int main()
{
concreteSubject *subjectA = new concreteSubject("目標A",1);
concreteSubject *subjectB = new concreteSubject("目標B",2);
concreteObserver *observer1 = new concreteObserver("1號觀察者", subjectA);
subjectA->addObserver(observer1);
concreteObserver *observer2 = new concreteObserver("2號觀察者", subjectA);
subjectA->addObserver(observer2);
concreteObserver *observer3 = new concreteObserver("3號觀察者", subjectB);
subjectB->addObserver(observer3);
concreteObserver *observer4 = new concreteObserver("4號觀察者", subjectB);
subjectB->addObserver(observer4);
subjectA->noticeAllObserver();
subjectB->noticeAllObserver();
//目標A,狀態 1:1號觀察者,2號觀察者
//目標B,狀態 2:3號觀察者,4號觀察者
cout << "-------------------修改目標A和目標B的狀態--------------------------" << endl;
subjectA->setStatus(3);
subjectB->setStatus(4);
subjectA->noticeAllObserver();
subjectB->noticeAllObserver();
//目標A,狀態 3:1號觀察者,2號觀察者
//目標B,狀態 4:3號觀察者,4號觀察者
cout << "-------------------讓1、3觀察A,讓2、4觀察B--------------------------" << endl;
subjectA->delObserver(observer2);
subjectB->delObserver(observer3);
subjectA->addObserver(observer3);
subjectB->addObserver(observer2);
subjectA->setStatus(5);
subjectB->setStatus(6);
subjectA->noticeAllObserver();
subjectB->noticeAllObserver();
//目標A,狀態 5:1號觀察者,3號觀察者
//目標B,狀態 6:2號觀察者,4號觀察者
return 0;
}